1use crate::error::*;
2use crate::{Decoder, Encoder};
3
4struct Base64Impl;
5
6#[derive(Copy, Clone, Debug, Eq, PartialEq)]
7enum Base64Variant {
8 Original = 1,
9 OriginalNoPadding = 3,
10 UrlSafe = 5,
11 UrlSafeNoPadding = 7,
12}
13
14enum VariantMask {
15 NoPadding = 2,
16 UrlSafe = 4,
17}
18
19impl Base64Impl {
20 #[inline]
21 fn _eq(x: u8, y: u8) -> u8 {
22 !(((0u16.wrapping_sub((x as u16) ^ (y as u16))) >> 8) as u8)
23 }
24
25 #[inline]
26 fn _gt(x: u8, y: u8) -> u8 {
27 (((y as u16).wrapping_sub(x as u16)) >> 8) as u8
28 }
29
30 #[inline]
31 fn _ge(x: u8, y: u8) -> u8 {
32 !Self::_gt(y, x)
33 }
34
35 #[inline]
36 fn _lt(x: u8, y: u8) -> u8 {
37 Self::_gt(y, x)
38 }
39
40 #[inline]
41 fn _le(x: u8, y: u8) -> u8 {
42 Self::_ge(y, x)
43 }
44
45 #[inline]
46 fn b64_byte_to_char(x: u8) -> u8 {
47 (Self::_lt(x, 26) & (x.wrapping_add(b'A')))
48 | (Self::_ge(x, 26) & Self::_lt(x, 52) & (x.wrapping_add(b'a'.wrapping_sub(26))))
49 | (Self::_ge(x, 52) & Self::_lt(x, 62) & (x.wrapping_add(b'0'.wrapping_sub(52))))
50 | (Self::_eq(x, 62) & b'+')
51 | (Self::_eq(x, 63) & b'/')
52 }
53
54 #[inline]
55 fn b64_char_to_byte(c: u8) -> u8 {
56 let x = (Self::_ge(c, b'A') & Self::_le(c, b'Z') & (c.wrapping_sub(b'A')))
57 | (Self::_ge(c, b'a') & Self::_le(c, b'z') & (c.wrapping_sub(b'a'.wrapping_sub(26))))
58 | (Self::_ge(c, b'0') & Self::_le(c, b'9') & (c.wrapping_sub(b'0'.wrapping_sub(52))))
59 | (Self::_eq(c, b'+') & 62)
60 | (Self::_eq(c, b'/') & 63);
61 x | (Self::_eq(x, 0) & (Self::_eq(c, b'A') ^ 0xff))
62 }
63
64 #[inline]
65 fn b64_byte_to_urlsafe_char(x: u8) -> u8 {
66 (Self::_lt(x, 26) & (x.wrapping_add(b'A')))
67 | (Self::_ge(x, 26) & Self::_lt(x, 52) & (x.wrapping_add(b'a'.wrapping_sub(26))))
68 | (Self::_ge(x, 52) & Self::_lt(x, 62) & (x.wrapping_add(b'0'.wrapping_sub(52))))
69 | (Self::_eq(x, 62) & b'-')
70 | (Self::_eq(x, 63) & b'_')
71 }
72
73 #[inline]
74 fn b64_urlsafe_char_to_byte(c: u8) -> u8 {
75 let x = (Self::_ge(c, b'A') & Self::_le(c, b'Z') & (c.wrapping_sub(b'A')))
76 | (Self::_ge(c, b'a') & Self::_le(c, b'z') & (c.wrapping_sub(b'a'.wrapping_sub(26))))
77 | (Self::_ge(c, b'0') & Self::_le(c, b'9') & (c.wrapping_sub(b'0'.wrapping_sub(52))))
78 | (Self::_eq(c, b'-') & 62)
79 | (Self::_eq(c, b'_') & 63);
80 x | (Self::_eq(x, 0) & (Self::_eq(c, b'A') ^ 0xff))
81 }
82
83 #[inline]
84 fn encoded_len(bin_len: usize, variant: Base64Variant) -> Result<usize, Error> {
85 let nibbles = bin_len / 3;
86 let rounded = nibbles * 3;
87 let pad = bin_len - rounded;
88 let mut b64_len = nibbles.checked_mul(4).ok_or(Error::Overflow)?;
89 if pad != 0 {
90 let remainder_len = if (variant as u16 & VariantMask::NoPadding as u16) == 0 {
91 4
92 } else {
93 2 + (pad >> 1)
94 };
95 b64_len = b64_len.checked_add(remainder_len).ok_or(Error::Overflow)?;
96 }
97 Ok(b64_len)
98 }
99
100 pub fn encode<'t>(
101 b64: &'t mut [u8],
102 bin: &[u8],
103 variant: Base64Variant,
104 ) -> Result<&'t [u8], Error> {
105 let bin_len = bin.len();
106 let b64_maxlen = b64.len();
107 let mut acc_len = 0usize;
108 let mut b64_pos = 0usize;
109 let mut acc = 0u16;
110
111 let nibbles = bin_len / 3;
112 let remainder = bin_len - 3 * nibbles;
113 let mut b64_len = nibbles * 4;
114 if remainder != 0 {
115 if (variant as u16 & VariantMask::NoPadding as u16) == 0 {
116 b64_len += 4;
117 } else {
118 b64_len += 2 + (remainder >> 1);
119 }
120 }
121 if b64_maxlen < b64_len {
122 return Err(Error::Overflow);
123 }
124 if (variant as u16 & VariantMask::UrlSafe as u16) != 0 {
125 for &v in bin {
126 acc = (acc << 8) + v as u16;
127 acc_len += 8;
128 while acc_len >= 6 {
129 acc_len -= 6;
130 b64[b64_pos] = Self::b64_byte_to_urlsafe_char(((acc >> acc_len) & 0x3f) as u8);
131 b64_pos += 1;
132 }
133 }
134 if acc_len > 0 {
135 b64[b64_pos] =
136 Self::b64_byte_to_urlsafe_char(((acc << (6 - acc_len)) & 0x3f) as u8);
137 b64_pos += 1;
138 }
139 } else {
140 for &v in bin {
141 acc = (acc << 8) + v as u16;
142 acc_len += 8;
143 while acc_len >= 6 {
144 acc_len -= 6;
145 b64[b64_pos] = Self::b64_byte_to_char(((acc >> acc_len) & 0x3f) as u8);
146 b64_pos += 1;
147 }
148 }
149 if acc_len > 0 {
150 b64[b64_pos] = Self::b64_byte_to_char(((acc << (6 - acc_len)) & 0x3f) as u8);
151 b64_pos += 1;
152 }
153 }
154 while b64_pos < b64_len {
155 b64[b64_pos] = b'=';
156 b64_pos += 1
157 }
158 Ok(&b64[..b64_pos])
159 }
160
161 fn skip_padding<'t>(
162 b64: &'t [u8],
163 mut padding_len: usize,
164 ignore: Option<&[u8]>,
165 ) -> Result<&'t [u8], Error> {
166 let b64_len = b64.len();
167 let mut b64_pos = 0usize;
168 while padding_len > 0 {
169 if b64_pos >= b64_len {
170 return Err(Error::InvalidInput);
171 }
172 let c = b64[b64_pos];
173 if c == b'=' {
174 padding_len -= 1
175 } else {
176 match ignore {
177 Some(ignore) if ignore.contains(&c) => {}
178 _ => return Err(Error::InvalidInput),
179 }
180 }
181 b64_pos += 1
182 }
183 Ok(&b64[b64_pos..])
184 }
185
186 pub fn decode<'t>(
187 bin: &'t mut [u8],
188 b64: &[u8],
189 ignore: Option<&[u8]>,
190 variant: Base64Variant,
191 ) -> Result<&'t [u8], Error> {
192 let bin_maxlen = bin.len();
193 let is_urlsafe = (variant as u16 & VariantMask::UrlSafe as u16) != 0;
194 let mut acc = 0u16;
195 let mut acc_len = 0usize;
196 let mut bin_pos = 0usize;
197 let mut premature_end = None;
198 for (b64_pos, &c) in b64.iter().enumerate() {
199 let d = if is_urlsafe {
200 Self::b64_urlsafe_char_to_byte(c)
201 } else {
202 Self::b64_char_to_byte(c)
203 };
204 if d == 0xff {
205 match ignore {
206 Some(ignore) if ignore.contains(&c) => continue,
207 _ => {
208 premature_end = Some(b64_pos);
209 break;
210 }
211 }
212 }
213 acc = (acc << 6) + d as u16;
214 acc_len += 6;
215 if acc_len >= 8 {
216 acc_len -= 8;
217 if bin_pos >= bin_maxlen {
218 return Err(Error::Overflow);
219 }
220 bin[bin_pos] = (acc >> acc_len) as u8;
221 bin_pos += 1;
222 }
223 }
224 if acc_len > 4 || (acc & ((1u16 << acc_len).wrapping_sub(1))) != 0 {
225 return Err(Error::InvalidInput);
226 }
227 let padding_len = acc_len / 2;
228 if let Some(premature_end) = premature_end {
229 let remaining = if variant as u16 & VariantMask::NoPadding as u16 == 0 {
230 Self::skip_padding(&b64[premature_end..], padding_len, ignore)?
231 } else {
232 &b64[premature_end..]
233 };
234 match ignore {
235 None => {
236 if !remaining.is_empty() {
237 return Err(Error::InvalidInput);
238 }
239 }
240 Some(ignore) => {
241 for &c in remaining {
242 if !ignore.contains(&c) {
243 return Err(Error::InvalidInput);
244 }
245 }
246 }
247 }
248 } else if variant as u16 & VariantMask::NoPadding as u16 == 0 && padding_len != 0 {
249 return Err(Error::InvalidInput);
250 }
251 Ok(&bin[..bin_pos])
252 }
253}
254
255pub struct Base64;
286
287pub struct Base64NoPadding;
309
310pub struct Base64UrlSafe;
344
345pub struct Base64UrlSafeNoPadding;
370
371impl Encoder for Base64 {
372 #[inline]
373 fn encoded_len(bin_len: usize) -> Result<usize, Error> {
374 Base64Impl::encoded_len(bin_len, Base64Variant::Original)
375 }
376
377 #[inline]
378 fn encode<IN: AsRef<[u8]>>(b64: &mut [u8], bin: IN) -> Result<&[u8], Error> {
379 Base64Impl::encode(b64, bin.as_ref(), Base64Variant::Original)
380 }
381}
382
383impl Decoder for Base64 {
384 #[inline]
385 fn decode<'t, IN: AsRef<[u8]>>(
386 bin: &'t mut [u8],
387 b64: IN,
388 ignore: Option<&[u8]>,
389 ) -> Result<&'t [u8], Error> {
390 Base64Impl::decode(bin, b64.as_ref(), ignore, Base64Variant::Original)
391 }
392}
393
394impl Encoder for Base64NoPadding {
395 #[inline]
396 fn encoded_len(bin_len: usize) -> Result<usize, Error> {
397 Base64Impl::encoded_len(bin_len, Base64Variant::OriginalNoPadding)
398 }
399
400 #[inline]
401 fn encode<IN: AsRef<[u8]>>(b64: &mut [u8], bin: IN) -> Result<&[u8], Error> {
402 Base64Impl::encode(b64, bin.as_ref(), Base64Variant::OriginalNoPadding)
403 }
404}
405
406impl Decoder for Base64NoPadding {
407 #[inline]
408 fn decode<'t, IN: AsRef<[u8]>>(
409 bin: &'t mut [u8],
410 b64: IN,
411 ignore: Option<&[u8]>,
412 ) -> Result<&'t [u8], Error> {
413 Base64Impl::decode(bin, b64.as_ref(), ignore, Base64Variant::OriginalNoPadding)
414 }
415}
416
417impl Encoder for Base64UrlSafe {
418 #[inline]
419 fn encoded_len(bin_len: usize) -> Result<usize, Error> {
420 Base64Impl::encoded_len(bin_len, Base64Variant::UrlSafe)
421 }
422
423 #[inline]
424 fn encode<IN: AsRef<[u8]>>(b64: &mut [u8], bin: IN) -> Result<&[u8], Error> {
425 Base64Impl::encode(b64, bin.as_ref(), Base64Variant::UrlSafe)
426 }
427}
428
429impl Decoder for Base64UrlSafe {
430 #[inline]
431 fn decode<'t, IN: AsRef<[u8]>>(
432 bin: &'t mut [u8],
433 b64: IN,
434 ignore: Option<&[u8]>,
435 ) -> Result<&'t [u8], Error> {
436 Base64Impl::decode(bin, b64.as_ref(), ignore, Base64Variant::UrlSafe)
437 }
438}
439
440impl Encoder for Base64UrlSafeNoPadding {
441 #[inline]
442 fn encoded_len(bin_len: usize) -> Result<usize, Error> {
443 Base64Impl::encoded_len(bin_len, Base64Variant::UrlSafeNoPadding)
444 }
445
446 #[inline]
447 fn encode<IN: AsRef<[u8]>>(b64: &mut [u8], bin: IN) -> Result<&[u8], Error> {
448 Base64Impl::encode(b64, bin.as_ref(), Base64Variant::UrlSafeNoPadding)
449 }
450}
451
452impl Decoder for Base64UrlSafeNoPadding {
453 #[inline]
454 fn decode<'t, IN: AsRef<[u8]>>(
455 bin: &'t mut [u8],
456 b64: IN,
457 ignore: Option<&[u8]>,
458 ) -> Result<&'t [u8], Error> {
459 Base64Impl::decode(bin, b64.as_ref(), ignore, Base64Variant::UrlSafeNoPadding)
460 }
461}
462
463#[cfg(feature = "std")]
464#[test]
465fn test_base64() {
466 let bin = [1u8, 5, 11, 15, 19, 131, 122];
467 let expected = "AQULDxODeg==";
468 let b64 = Base64::encode_to_string(bin).unwrap();
469 assert_eq!(b64, expected);
470 let bin2 = Base64::decode_to_vec(&b64, None).unwrap();
471 assert_eq!(bin, &bin2[..]);
472}
473
474#[cfg(feature = "std")]
475#[test]
476fn test_base64_mising_padding() {
477 let missing_padding = "AA";
478 assert!(Base64::decode_to_vec(missing_padding, None).is_err());
479 assert!(Base64NoPadding::decode_to_vec(missing_padding, None).is_ok());
480 let missing_padding = "AAA";
481 assert!(Base64::decode_to_vec(missing_padding, None).is_err());
482 assert!(Base64NoPadding::decode_to_vec(missing_padding, None).is_ok());
483}
484
485#[test]
486fn test_base64_no_std() {
487 let bin = [1u8, 5, 11, 15, 19, 131, 122];
488 let expected = [65, 81, 85, 76, 68, 120, 79, 68, 101, 103, 61, 61];
489 let mut b64 = [0u8; 12];
490 let b64 = Base64::encode(&mut b64, bin).unwrap();
491 assert_eq!(b64, expected);
492 let mut bin2 = [0u8; 7];
493 let bin2 = Base64::decode(&mut bin2, b64, None).unwrap();
494 assert_eq!(bin, bin2);
495}
496
497#[cfg(feature = "std")]
498#[test]
499fn test_base64_invalid_padding() {
500 let valid_padding = "AA==";
501 assert_eq!(Base64::decode_to_vec(valid_padding, None), Ok(vec![0u8; 1]));
502 let invalid_padding = "AA=";
503 assert_eq!(
504 Base64::decode_to_vec(invalid_padding, None),
505 Err(Error::InvalidInput)
506 );
507}
508
509#[cfg(feature = "std")]
510#[test]
511fn test_base64_non_canonical() {
512 assert!(Base64::decode_to_vec("AB==", None).is_err());
513 assert!(Base64NoPadding::decode_to_vec("AB", None).is_err());
514}
515
516#[cfg(feature = "std")]
517#[test]
518fn test_base64_no_padding_rejects_padding() {
519 assert!(Base64NoPadding::decode_to_vec("AA==", None).is_err());
520 assert!(Base64NoPadding::decode_to_vec("AAA=", None).is_err());
521}