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 Ok(nibbles.checked_mul(4).ok_or(Error::Overflow)?
89 + ((pad | (pad >> 1)) & 1)
90 * (4 - (!((((variant as usize) & 2) >> 1).wrapping_sub(1)) & (3 - pad)))
91 + 1)
92 }
93
94 pub fn encode<'t>(
95 b64: &'t mut [u8],
96 bin: &[u8],
97 variant: Base64Variant,
98 ) -> Result<&'t [u8], Error> {
99 let bin_len = bin.len();
100 let b64_maxlen = b64.len();
101 let mut acc_len = 0usize;
102 let mut b64_pos = 0usize;
103 let mut acc = 0u16;
104
105 let nibbles = bin_len / 3;
106 let remainder = bin_len - 3 * nibbles;
107 let mut b64_len = nibbles * 4;
108 if remainder != 0 {
109 if (variant as u16 & VariantMask::NoPadding as u16) == 0 {
110 b64_len += 4;
111 } else {
112 b64_len += 2 + (remainder >> 1);
113 }
114 }
115 if b64_maxlen < b64_len {
116 return Err(Error::Overflow);
117 }
118 if (variant as u16 & VariantMask::UrlSafe as u16) != 0 {
119 for &v in bin {
120 acc = (acc << 8) + v as u16;
121 acc_len += 8;
122 while acc_len >= 6 {
123 acc_len -= 6;
124 b64[b64_pos] = Self::b64_byte_to_urlsafe_char(((acc >> acc_len) & 0x3f) as u8);
125 b64_pos += 1;
126 }
127 }
128 if acc_len > 0 {
129 b64[b64_pos] =
130 Self::b64_byte_to_urlsafe_char(((acc << (6 - acc_len)) & 0x3f) as u8);
131 b64_pos += 1;
132 }
133 } else {
134 for &v in bin {
135 acc = (acc << 8) + v as u16;
136 acc_len += 8;
137 while acc_len >= 6 {
138 acc_len -= 6;
139 b64[b64_pos] = Self::b64_byte_to_char(((acc >> acc_len) & 0x3f) as u8);
140 b64_pos += 1;
141 }
142 }
143 if acc_len > 0 {
144 b64[b64_pos] = Self::b64_byte_to_char(((acc << (6 - acc_len)) & 0x3f) as u8);
145 b64_pos += 1;
146 }
147 }
148 while b64_pos < b64_len {
149 b64[b64_pos] = b'=';
150 b64_pos += 1
151 }
152 Ok(&b64[..b64_pos])
153 }
154
155 fn skip_padding<'t>(
156 b64: &'t [u8],
157 mut padding_len: usize,
158 ignore: Option<&[u8]>,
159 ) -> Result<&'t [u8], Error> {
160 let b64_len = b64.len();
161 let mut b64_pos = 0usize;
162 while padding_len > 0 {
163 if b64_pos >= b64_len {
164 return Err(Error::InvalidInput);
165 }
166 let c = b64[b64_pos];
167 if c == b'=' {
168 padding_len -= 1
169 } else {
170 match ignore {
171 Some(ignore) if ignore.contains(&c) => {}
172 _ => return Err(Error::InvalidInput),
173 }
174 }
175 b64_pos += 1
176 }
177 Ok(&b64[b64_pos..])
178 }
179
180 pub fn decode<'t>(
181 bin: &'t mut [u8],
182 b64: &[u8],
183 ignore: Option<&[u8]>,
184 variant: Base64Variant,
185 ) -> Result<&'t [u8], Error> {
186 let bin_maxlen = bin.len();
187 let is_urlsafe = (variant as u16 & VariantMask::UrlSafe as u16) != 0;
188 let mut acc = 0u16;
189 let mut acc_len = 0usize;
190 let mut bin_pos = 0usize;
191 let mut premature_end = None;
192 for (b64_pos, &c) in b64.iter().enumerate() {
193 let d = if is_urlsafe {
194 Self::b64_urlsafe_char_to_byte(c)
195 } else {
196 Self::b64_char_to_byte(c)
197 };
198 if d == 0xff {
199 match ignore {
200 Some(ignore) if ignore.contains(&c) => continue,
201 _ => {
202 premature_end = Some(b64_pos);
203 break;
204 }
205 }
206 }
207 acc = (acc << 6) + d as u16;
208 acc_len += 6;
209 if acc_len >= 8 {
210 acc_len -= 8;
211 if bin_pos >= bin_maxlen {
212 return Err(Error::Overflow);
213 }
214 bin[bin_pos] = (acc >> acc_len) as u8;
215 bin_pos += 1;
216 }
217 }
218 if acc_len > 4 || (acc & ((1u16 << acc_len).wrapping_sub(1))) != 0 {
219 return Err(Error::InvalidInput);
220 }
221 let padding_len = acc_len / 2;
222 if let Some(premature_end) = premature_end {
223 let remaining = if variant as u16 & VariantMask::NoPadding as u16 == 0 {
224 Self::skip_padding(&b64[premature_end..], padding_len, ignore)?
225 } else {
226 &b64[premature_end..]
227 };
228 match ignore {
229 None => {
230 if !remaining.is_empty() {
231 return Err(Error::InvalidInput);
232 }
233 }
234 Some(ignore) => {
235 for &c in remaining {
236 if !ignore.contains(&c) {
237 return Err(Error::InvalidInput);
238 }
239 }
240 }
241 }
242 } else if variant as u16 & VariantMask::NoPadding as u16 == 0 && padding_len != 0 {
243 return Err(Error::InvalidInput);
244 }
245 Ok(&bin[..bin_pos])
246 }
247}
248
249pub struct Base64;
278
279pub struct Base64NoPadding;
299
300pub struct Base64UrlSafe;
332
333pub struct Base64UrlSafeNoPadding;
356
357impl Encoder for Base64 {
358 #[inline]
359 fn encoded_len(bin_len: usize) -> Result<usize, Error> {
360 Base64Impl::encoded_len(bin_len, Base64Variant::Original)
361 }
362
363 #[inline]
364 fn encode<IN: AsRef<[u8]>>(b64: &mut [u8], bin: IN) -> Result<&[u8], Error> {
365 Base64Impl::encode(b64, bin.as_ref(), Base64Variant::Original)
366 }
367}
368
369impl Decoder for Base64 {
370 #[inline]
371 fn decode<'t, IN: AsRef<[u8]>>(
372 bin: &'t mut [u8],
373 b64: IN,
374 ignore: Option<&[u8]>,
375 ) -> Result<&'t [u8], Error> {
376 Base64Impl::decode(bin, b64.as_ref(), ignore, Base64Variant::Original)
377 }
378}
379
380impl Encoder for Base64NoPadding {
381 #[inline]
382 fn encoded_len(bin_len: usize) -> Result<usize, Error> {
383 Base64Impl::encoded_len(bin_len, Base64Variant::OriginalNoPadding)
384 }
385
386 #[inline]
387 fn encode<IN: AsRef<[u8]>>(b64: &mut [u8], bin: IN) -> Result<&[u8], Error> {
388 Base64Impl::encode(b64, bin.as_ref(), Base64Variant::OriginalNoPadding)
389 }
390}
391
392impl Decoder for Base64NoPadding {
393 #[inline]
394 fn decode<'t, IN: AsRef<[u8]>>(
395 bin: &'t mut [u8],
396 b64: IN,
397 ignore: Option<&[u8]>,
398 ) -> Result<&'t [u8], Error> {
399 Base64Impl::decode(bin, b64.as_ref(), ignore, Base64Variant::OriginalNoPadding)
400 }
401}
402
403impl Encoder for Base64UrlSafe {
404 #[inline]
405 fn encoded_len(bin_len: usize) -> Result<usize, Error> {
406 Base64Impl::encoded_len(bin_len, Base64Variant::UrlSafe)
407 }
408
409 #[inline]
410 fn encode<IN: AsRef<[u8]>>(b64: &mut [u8], bin: IN) -> Result<&[u8], Error> {
411 Base64Impl::encode(b64, bin.as_ref(), Base64Variant::UrlSafe)
412 }
413}
414
415impl Decoder for Base64UrlSafe {
416 #[inline]
417 fn decode<'t, IN: AsRef<[u8]>>(
418 bin: &'t mut [u8],
419 b64: IN,
420 ignore: Option<&[u8]>,
421 ) -> Result<&'t [u8], Error> {
422 Base64Impl::decode(bin, b64.as_ref(), ignore, Base64Variant::UrlSafe)
423 }
424}
425
426impl Encoder for Base64UrlSafeNoPadding {
427 #[inline]
428 fn encoded_len(bin_len: usize) -> Result<usize, Error> {
429 Base64Impl::encoded_len(bin_len, Base64Variant::UrlSafeNoPadding)
430 }
431
432 #[inline]
433 fn encode<IN: AsRef<[u8]>>(b64: &mut [u8], bin: IN) -> Result<&[u8], Error> {
434 Base64Impl::encode(b64, bin.as_ref(), Base64Variant::UrlSafeNoPadding)
435 }
436}
437
438impl Decoder for Base64UrlSafeNoPadding {
439 #[inline]
440 fn decode<'t, IN: AsRef<[u8]>>(
441 bin: &'t mut [u8],
442 b64: IN,
443 ignore: Option<&[u8]>,
444 ) -> Result<&'t [u8], Error> {
445 Base64Impl::decode(bin, b64.as_ref(), ignore, Base64Variant::UrlSafeNoPadding)
446 }
447}
448
449#[cfg(feature = "std")]
450#[test]
451fn test_base64() {
452 let bin = [1u8, 5, 11, 15, 19, 131, 122];
453 let expected = "AQULDxODeg==";
454 let b64 = Base64::encode_to_string(bin).unwrap();
455 assert_eq!(b64, expected);
456 let bin2 = Base64::decode_to_vec(&b64, None).unwrap();
457 assert_eq!(bin, &bin2[..]);
458}
459
460#[cfg(feature = "std")]
461#[test]
462fn test_base64_mising_padding() {
463 let missing_padding = "AA";
464 assert!(Base64::decode_to_vec(missing_padding, None).is_err());
465 assert!(Base64NoPadding::decode_to_vec(missing_padding, None).is_ok());
466 let missing_padding = "AAA";
467 assert!(Base64::decode_to_vec(missing_padding, None).is_err());
468 assert!(Base64NoPadding::decode_to_vec(missing_padding, None).is_ok());
469}
470
471#[test]
472fn test_base64_no_std() {
473 let bin = [1u8, 5, 11, 15, 19, 131, 122];
474 let expected = [65, 81, 85, 76, 68, 120, 79, 68, 101, 103, 61, 61];
475 let mut b64 = [0u8; 12];
476 let b64 = Base64::encode(&mut b64, bin).unwrap();
477 assert_eq!(b64, expected);
478 let mut bin2 = [0u8; 7];
479 let bin2 = Base64::decode(&mut bin2, b64, None).unwrap();
480 assert_eq!(bin, bin2);
481}
482
483#[test]
484fn test_base64_invalid_padding() {
485 let valid_padding = "AA==";
486 assert_eq!(Base64::decode_to_vec(valid_padding, None), Ok(vec![0u8; 1]));
487 let invalid_padding = "AA=";
488 assert_eq!(
489 Base64::decode_to_vec(invalid_padding, None),
490 Err(Error::InvalidInput)
491 );
492}