1#![cfg_attr(not(feature = "std"), no_std)]
2#![deny(unsafe_code)]
3#![deny(missing_docs)]
4#![deny(clippy::all)]
5#![deny(clippy::pedantic)]
6#![allow(clippy::missing_errors_doc)]
7
8#[cfg(feature = "alloc")]
47extern crate alloc;
48
49#[cfg(feature = "simd")]
50mod simd;
51
52pub mod runtime {
58 #[derive(Clone, Copy, Debug, Eq, PartialEq)]
60 #[non_exhaustive]
61 pub enum Backend {
62 Scalar,
64 Avx512Vbmi,
66 Avx2,
68 Ssse3Sse41,
70 Neon,
72 WasmSimd128,
74 }
75
76 impl Backend {
77 #[must_use]
83 pub const fn as_str(self) -> &'static str {
84 match self {
85 Self::Scalar => "scalar",
86 Self::Avx512Vbmi => "avx512-vbmi",
87 Self::Avx2 => "avx2",
88 Self::Ssse3Sse41 => "ssse3-sse4.1",
89 Self::Neon => "neon",
90 Self::WasmSimd128 => "wasm-simd128",
91 }
92 }
93
94 #[must_use]
107 pub const fn required_cpu_features(self) -> &'static [&'static str] {
108 match self {
109 Self::Scalar => &[],
110 Self::Avx512Vbmi => &["avx512f", "avx512bw", "avx512vl", "avx512vbmi"],
111 Self::Avx2 => &["avx2"],
112 Self::Ssse3Sse41 => &["ssse3", "sse4.1"],
113 Self::Neon => &["neon"],
114 Self::WasmSimd128 => &["simd128"],
115 }
116 }
117 }
118
119 impl core::fmt::Display for Backend {
120 fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
121 formatter.write_str(self.as_str())
122 }
123 }
124
125 #[derive(Clone, Copy, Debug, Eq, PartialEq)]
127 #[non_exhaustive]
128 pub enum SecurityPosture {
129 ScalarOnly,
131 SimdCandidateScalarActive,
133 Accelerated,
135 }
136
137 impl SecurityPosture {
138 #[must_use]
147 pub const fn as_str(self) -> &'static str {
148 match self {
149 Self::ScalarOnly => "scalar-only",
150 Self::SimdCandidateScalarActive => "simd-candidate-scalar-active",
151 Self::Accelerated => "accelerated",
152 }
153 }
154 }
155
156 impl core::fmt::Display for SecurityPosture {
157 fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
158 formatter.write_str(self.as_str())
159 }
160 }
161
162 #[derive(Clone, Copy, Debug, Eq, PartialEq)]
164 #[non_exhaustive]
165 pub enum BackendPolicy {
166 ScalarExecutionOnly,
168 SimdFeatureDisabled,
170 NoDetectedSimdCandidate,
172 HighAssuranceScalarOnly,
175 }
176
177 impl BackendPolicy {
178 #[must_use]
187 pub const fn as_str(self) -> &'static str {
188 match self {
189 Self::ScalarExecutionOnly => "scalar-execution-only",
190 Self::SimdFeatureDisabled => "simd-feature-disabled",
191 Self::NoDetectedSimdCandidate => "no-detected-simd-candidate",
192 Self::HighAssuranceScalarOnly => "high-assurance-scalar-only",
193 }
194 }
195 }
196
197 impl core::fmt::Display for BackendPolicy {
198 fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
199 formatter.write_str(self.as_str())
200 }
201 }
202
203 #[derive(Clone, Copy, Debug, Eq, PartialEq)]
205 pub struct BackendPolicyError {
206 pub policy: BackendPolicy,
208 pub report: BackendReport,
210 }
211
212 impl core::fmt::Display for BackendPolicyError {
213 fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
214 write!(
215 formatter,
216 "runtime backend policy `{}` was not satisfied ({})",
217 self.policy, self.report,
218 )
219 }
220 }
221
222 #[cfg(feature = "std")]
223 impl std::error::Error for BackendPolicyError {}
224
225 #[derive(Clone, Copy, Debug, Eq, PartialEq)]
227 pub struct BackendReport {
228 pub active: Backend,
230 pub candidate: Backend,
232 pub simd_feature_enabled: bool,
234 pub accelerated_backend_active: bool,
236 pub unsafe_boundary_enforced: bool,
238 pub security_posture: SecurityPosture,
240 }
241
242 #[derive(Clone, Copy, Debug, Eq, PartialEq)]
244 pub struct BackendSnapshot {
245 pub active: &'static str,
247 pub candidate: &'static str,
249 pub candidate_required_cpu_features: &'static [&'static str],
251 pub simd_feature_enabled: bool,
253 pub accelerated_backend_active: bool,
255 pub unsafe_boundary_enforced: bool,
257 pub security_posture: &'static str,
259 }
260
261 impl core::fmt::Display for BackendReport {
262 fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
263 write!(
264 formatter,
265 "active={} candidate={} candidate_required_cpu_features=",
266 self.active, self.candidate,
267 )?;
268 write_feature_list(formatter, self.candidate_required_cpu_features())?;
269 write!(
270 formatter,
271 " simd_feature_enabled={} accelerated_backend_active={} unsafe_boundary_enforced={} security_posture={}",
272 self.simd_feature_enabled,
273 self.accelerated_backend_active,
274 self.unsafe_boundary_enforced,
275 self.security_posture,
276 )
277 }
278 }
279
280 impl BackendReport {
281 #[must_use]
291 pub const fn satisfies(self, policy: BackendPolicy) -> bool {
292 match policy {
293 BackendPolicy::ScalarExecutionOnly => {
294 matches!(self.active, Backend::Scalar) && !self.accelerated_backend_active
295 }
296 BackendPolicy::SimdFeatureDisabled => !self.simd_feature_enabled,
297 BackendPolicy::NoDetectedSimdCandidate => matches!(self.candidate, Backend::Scalar),
298 BackendPolicy::HighAssuranceScalarOnly => {
299 matches!(self.active, Backend::Scalar)
300 && matches!(self.candidate, Backend::Scalar)
301 && !self.simd_feature_enabled
302 && !self.accelerated_backend_active
303 && self.unsafe_boundary_enforced
304 }
305 }
306 }
307
308 #[must_use]
319 pub const fn candidate_required_cpu_features(self) -> &'static [&'static str] {
320 self.candidate.required_cpu_features()
321 }
322
323 #[must_use]
332 pub const fn snapshot(self) -> BackendSnapshot {
333 BackendSnapshot {
334 active: self.active.as_str(),
335 candidate: self.candidate.as_str(),
336 candidate_required_cpu_features: self.candidate_required_cpu_features(),
337 simd_feature_enabled: self.simd_feature_enabled,
338 accelerated_backend_active: self.accelerated_backend_active,
339 unsafe_boundary_enforced: self.unsafe_boundary_enforced,
340 security_posture: self.security_posture.as_str(),
341 }
342 }
343 }
344
345 #[must_use]
354 pub fn backend_report() -> BackendReport {
355 let active = active_backend();
356 let candidate = detected_candidate();
357 let accelerated_backend_active = active != Backend::Scalar;
358 let security_posture = if accelerated_backend_active {
359 SecurityPosture::Accelerated
360 } else if candidate != Backend::Scalar {
361 SecurityPosture::SimdCandidateScalarActive
362 } else {
363 SecurityPosture::ScalarOnly
364 };
365
366 BackendReport {
367 active,
368 candidate,
369 simd_feature_enabled: cfg!(feature = "simd"),
370 accelerated_backend_active,
371 unsafe_boundary_enforced: true,
372 security_posture,
373 }
374 }
375
376 pub fn require_backend_policy(policy: BackendPolicy) -> Result<(), BackendPolicyError> {
385 let report = backend_report();
386 if report.satisfies(policy) {
387 Ok(())
388 } else {
389 Err(BackendPolicyError { policy, report })
390 }
391 }
392
393 fn write_feature_list(
394 formatter: &mut core::fmt::Formatter<'_>,
395 features: &[&str],
396 ) -> core::fmt::Result {
397 formatter.write_str("[")?;
398 let mut index = 0;
399 while index < features.len() {
400 if index != 0 {
401 formatter.write_str(",")?;
402 }
403 formatter.write_str(features[index])?;
404 index += 1;
405 }
406 formatter.write_str("]")
407 }
408
409 #[cfg(feature = "simd")]
410 fn active_backend() -> Backend {
411 match super::simd::active_backend() {
412 super::simd::ActiveBackend::Scalar => Backend::Scalar,
413 }
414 }
415
416 #[cfg(not(feature = "simd"))]
417 const fn active_backend() -> Backend {
418 Backend::Scalar
419 }
420
421 #[cfg(feature = "simd")]
422 fn detected_candidate() -> Backend {
423 match super::simd::detected_candidate() {
424 super::simd::Candidate::Scalar => Backend::Scalar,
425 #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
426 super::simd::Candidate::Avx512Vbmi => Backend::Avx512Vbmi,
427 #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
428 super::simd::Candidate::Avx2 => Backend::Avx2,
429 #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
430 super::simd::Candidate::Ssse3Sse41 => Backend::Ssse3Sse41,
431 #[cfg(any(target_arch = "aarch64", target_arch = "arm"))]
432 super::simd::Candidate::Neon => Backend::Neon,
433 #[cfg(target_arch = "wasm32")]
434 super::simd::Candidate::WasmSimd128 => Backend::WasmSimd128,
435 }
436 }
437
438 #[cfg(not(feature = "simd"))]
439 const fn detected_candidate() -> Backend {
440 Backend::Scalar
441 }
442}
443
444#[cfg(feature = "stream")]
445pub mod stream {
446 use super::{Alphabet, DecodeError, EncodeError, Engine};
476 use std::io::{self, Read, Write};
477
478 struct OutputQueue<const CAP: usize> {
479 buffer: [u8; CAP],
480 start: usize,
481 len: usize,
482 }
483
484 impl<const CAP: usize> OutputQueue<CAP> {
485 const fn new() -> Self {
486 Self {
487 buffer: [0; CAP],
488 start: 0,
489 len: 0,
490 }
491 }
492
493 const fn is_empty(&self) -> bool {
494 self.len == 0
495 }
496
497 fn push_slice(&mut self, input: &[u8]) -> io::Result<()> {
498 if input.len() > self.available_capacity() {
499 return Err(io::Error::new(
500 io::ErrorKind::InvalidInput,
501 "base64 stream output queue capacity exceeded",
502 ));
503 }
504
505 let mut read = 0;
506 while read < input.len() {
507 let write = (self.start + self.len) % CAP;
508 self.buffer[write] = input[read];
509 self.len += 1;
510 read += 1;
511 }
512
513 Ok(())
514 }
515
516 fn pop_front(&mut self) -> Option<u8> {
517 if self.len == 0 {
518 return None;
519 }
520
521 let byte = self.buffer[self.start];
522 self.buffer[self.start] = 0;
523 self.start = (self.start + 1) % CAP;
524 self.len -= 1;
525 if self.len == 0 {
526 self.start = 0;
527 }
528 Some(byte)
529 }
530
531 fn clear_all(&mut self) {
532 crate::wipe_bytes(&mut self.buffer);
533 self.start = 0;
534 self.len = 0;
535 }
536
537 const fn available_capacity(&self) -> usize {
538 CAP - self.len
539 }
540 }
541
542 pub struct Encoder<W, A, const PAD: bool>
544 where
545 A: Alphabet,
546 {
547 inner: Option<W>,
548 engine: Engine<A, PAD>,
549 pending: [u8; 2],
550 pending_len: usize,
551 }
552
553 impl<W, A, const PAD: bool> Encoder<W, A, PAD>
554 where
555 A: Alphabet,
556 {
557 #[must_use]
559 pub const fn new(inner: W, engine: Engine<A, PAD>) -> Self {
560 Self {
561 inner: Some(inner),
562 engine,
563 pending: [0; 2],
564 pending_len: 0,
565 }
566 }
567
568 #[must_use]
570 pub fn get_ref(&self) -> &W {
571 self.inner_ref()
572 }
573
574 pub fn get_mut(&mut self) -> &mut W {
576 self.inner_mut()
577 }
578
579 #[must_use]
583 pub fn into_inner(mut self) -> W {
584 self.take_inner()
585 }
586
587 fn inner_ref(&self) -> &W {
588 match &self.inner {
589 Some(inner) => inner,
590 None => unreachable!("stream encoder inner writer was already taken"),
591 }
592 }
593
594 fn inner_mut(&mut self) -> &mut W {
595 match &mut self.inner {
596 Some(inner) => inner,
597 None => unreachable!("stream encoder inner writer was already taken"),
598 }
599 }
600
601 fn take_inner(&mut self) -> W {
602 match self.inner.take() {
603 Some(inner) => inner,
604 None => unreachable!("stream encoder inner writer was already taken"),
605 }
606 }
607
608 fn clear_pending(&mut self) {
609 crate::wipe_bytes(&mut self.pending);
610 self.pending_len = 0;
611 }
612 }
613
614 impl<W, A, const PAD: bool> Drop for Encoder<W, A, PAD>
615 where
616 A: Alphabet,
617 {
618 fn drop(&mut self) {
619 self.clear_pending();
620 }
621 }
622
623 impl<W, A, const PAD: bool> Encoder<W, A, PAD>
624 where
625 W: Write,
626 A: Alphabet,
627 {
628 pub fn finish(mut self) -> io::Result<W> {
630 self.write_pending_final()?;
631 self.inner_mut().flush()?;
632 Ok(self.take_inner())
633 }
634
635 fn write_pending_final(&mut self) -> io::Result<()> {
636 if self.pending_len == 0 {
637 return Ok(());
638 }
639
640 let mut pending = [0u8; 2];
641 pending[..self.pending_len].copy_from_slice(&self.pending[..self.pending_len]);
642 let pending_len = self.pending_len;
643 let mut encoded = [0u8; 4];
644 let result = self.write_encoded_temp(&pending[..pending_len], &mut encoded);
645 crate::wipe_bytes(&mut pending);
646 result?;
647 self.clear_pending();
648 Ok(())
649 }
650
651 fn write_encoded_temp(&mut self, input: &[u8], encoded: &mut [u8]) -> io::Result<()> {
652 let written = match self.engine.encode_slice(input, encoded) {
653 Ok(written) => written,
654 Err(err) => {
655 crate::wipe_bytes(encoded);
656 return Err(encode_error_to_io(err));
657 }
658 };
659
660 let result = self.inner_mut().write_all(&encoded[..written]);
661 crate::wipe_bytes(encoded);
662 result
663 }
664 }
665
666 impl<W, A, const PAD: bool> Write for Encoder<W, A, PAD>
667 where
668 W: Write,
669 A: Alphabet,
670 {
671 fn write(&mut self, input: &[u8]) -> io::Result<usize> {
672 if input.is_empty() {
673 return Ok(0);
674 }
675
676 let mut consumed = 0;
677 if self.pending_len > 0 {
678 let needed = 3 - self.pending_len;
679 if input.len() < needed {
680 self.pending[self.pending_len..self.pending_len + input.len()]
681 .copy_from_slice(input);
682 self.pending_len += input.len();
683 return Ok(input.len());
684 }
685
686 let mut chunk = [0u8; 3];
687 chunk[..self.pending_len].copy_from_slice(&self.pending[..self.pending_len]);
688 chunk[self.pending_len..].copy_from_slice(&input[..needed]);
689
690 let mut encoded = [0u8; 4];
691 let result = self.write_encoded_temp(&chunk, &mut encoded);
692 crate::wipe_bytes(&mut chunk);
693 result?;
694 self.clear_pending();
695 consumed += needed;
696 }
697
698 let remaining = &input[consumed..];
699 let full_len = remaining.len() / 3 * 3;
700 let mut offset = 0;
701 let mut encoded = [0u8; 1024];
702 while offset < full_len {
703 let mut take = core::cmp::min(full_len - offset, 768);
704 take -= take % 3;
705 debug_assert!(take > 0);
706
707 self.write_encoded_temp(&remaining[offset..offset + take], &mut encoded)?;
708 offset += take;
709 }
710
711 let tail = &remaining[full_len..];
712 self.pending[..tail.len()].copy_from_slice(tail);
713 self.pending_len = tail.len();
714
715 Ok(input.len())
716 }
717
718 fn flush(&mut self) -> io::Result<()> {
719 self.inner_mut().flush()
720 }
721 }
722
723 fn encode_error_to_io(err: EncodeError) -> io::Error {
724 io::Error::new(io::ErrorKind::InvalidInput, err)
725 }
726
727 pub struct Decoder<W, A, const PAD: bool>
729 where
730 A: Alphabet,
731 {
732 inner: Option<W>,
733 engine: Engine<A, PAD>,
734 pending: [u8; 4],
735 pending_len: usize,
736 finished: bool,
737 }
738
739 impl<W, A, const PAD: bool> Decoder<W, A, PAD>
740 where
741 A: Alphabet,
742 {
743 #[must_use]
745 pub const fn new(inner: W, engine: Engine<A, PAD>) -> Self {
746 Self {
747 inner: Some(inner),
748 engine,
749 pending: [0; 4],
750 pending_len: 0,
751 finished: false,
752 }
753 }
754
755 #[must_use]
757 pub fn get_ref(&self) -> &W {
758 self.inner_ref()
759 }
760
761 pub fn get_mut(&mut self) -> &mut W {
763 self.inner_mut()
764 }
765
766 #[must_use]
770 pub fn into_inner(mut self) -> W {
771 self.take_inner()
772 }
773
774 fn inner_ref(&self) -> &W {
775 match &self.inner {
776 Some(inner) => inner,
777 None => unreachable!("stream decoder inner writer was already taken"),
778 }
779 }
780
781 fn inner_mut(&mut self) -> &mut W {
782 match &mut self.inner {
783 Some(inner) => inner,
784 None => unreachable!("stream decoder inner writer was already taken"),
785 }
786 }
787
788 fn take_inner(&mut self) -> W {
789 match self.inner.take() {
790 Some(inner) => inner,
791 None => unreachable!("stream decoder inner writer was already taken"),
792 }
793 }
794
795 fn clear_pending(&mut self) {
796 crate::wipe_bytes(&mut self.pending);
797 self.pending_len = 0;
798 }
799 }
800
801 impl<W, A, const PAD: bool> Drop for Decoder<W, A, PAD>
802 where
803 A: Alphabet,
804 {
805 fn drop(&mut self) {
806 self.clear_pending();
807 }
808 }
809
810 impl<W, A, const PAD: bool> Decoder<W, A, PAD>
811 where
812 W: Write,
813 A: Alphabet,
814 {
815 pub fn finish(mut self) -> io::Result<W> {
817 self.write_pending_final()?;
818 self.inner_mut().flush()?;
819 Ok(self.take_inner())
820 }
821
822 fn write_pending_final(&mut self) -> io::Result<()> {
823 if self.pending_len == 0 {
824 return Ok(());
825 }
826
827 let mut pending = [0u8; 4];
828 pending[..self.pending_len].copy_from_slice(&self.pending[..self.pending_len]);
829 let pending_len = self.pending_len;
830 let mut decoded = [0u8; 3];
831 let result = self.write_decoded_temp(&pending[..pending_len], &mut decoded);
832 crate::wipe_bytes(&mut pending);
833 result?;
834 self.clear_pending();
835 Ok(())
836 }
837
838 fn write_full_quad(&mut self, mut input: [u8; 4]) -> io::Result<()> {
839 let mut decoded = [0u8; 3];
840 let result = self.write_decoded_temp(&input, &mut decoded);
841 crate::wipe_bytes(&mut input);
842 let written = result?;
843 if written < 3 {
844 self.finished = true;
845 }
846 Ok(())
847 }
848
849 fn write_decoded_temp(&mut self, input: &[u8], decoded: &mut [u8]) -> io::Result<usize> {
850 let written = match self.engine.decode_slice(input, decoded) {
851 Ok(written) => written,
852 Err(err) => {
853 crate::wipe_bytes(decoded);
854 return Err(decode_error_to_io(err));
855 }
856 };
857
858 let result = self.inner_mut().write_all(&decoded[..written]);
859 crate::wipe_bytes(decoded);
860 match result {
861 Ok(()) => Ok(written),
862 Err(err) => Err(err),
863 }
864 }
865 }
866
867 impl<W, A, const PAD: bool> Write for Decoder<W, A, PAD>
868 where
869 W: Write,
870 A: Alphabet,
871 {
872 fn write(&mut self, input: &[u8]) -> io::Result<usize> {
873 if input.is_empty() {
874 return Ok(0);
875 }
876 if self.finished {
877 return Err(trailing_input_after_padding_error());
878 }
879
880 let mut consumed = 0;
881 if self.pending_len > 0 {
882 let needed = 4 - self.pending_len;
883 if input.len() < needed {
884 self.pending[self.pending_len..self.pending_len + input.len()]
885 .copy_from_slice(input);
886 self.pending_len += input.len();
887 return Ok(input.len());
888 }
889
890 let mut quad = [0u8; 4];
891 quad[..self.pending_len].copy_from_slice(&self.pending[..self.pending_len]);
892 quad[self.pending_len..].copy_from_slice(&input[..needed]);
893 let result = self.write_full_quad(quad);
894 crate::wipe_bytes(&mut quad);
895 result?;
896 self.clear_pending();
897 consumed += needed;
898 if self.finished && consumed < input.len() {
899 return Err(trailing_input_after_padding_error());
900 }
901 }
902
903 let remaining = &input[consumed..];
904 let full_len = remaining.len() / 4 * 4;
905 let mut offset = 0;
906 while offset < full_len {
907 let quad = [
908 remaining[offset],
909 remaining[offset + 1],
910 remaining[offset + 2],
911 remaining[offset + 3],
912 ];
913 let mut quad = quad;
914 let result = self.write_full_quad(quad);
915 crate::wipe_bytes(&mut quad);
916 result?;
917 offset += 4;
918 if self.finished && offset < remaining.len() {
919 return Err(trailing_input_after_padding_error());
920 }
921 }
922
923 let tail = &remaining[full_len..];
924 self.pending[..tail.len()].copy_from_slice(tail);
925 self.pending_len = tail.len();
926
927 Ok(input.len())
928 }
929
930 fn flush(&mut self) -> io::Result<()> {
931 self.inner_mut().flush()
932 }
933 }
934
935 fn decode_error_to_io(err: DecodeError) -> io::Error {
936 io::Error::new(io::ErrorKind::InvalidInput, err)
937 }
938
939 fn trailing_input_after_padding_error() -> io::Error {
940 io::Error::new(
941 io::ErrorKind::InvalidInput,
942 "base64 decoder received trailing input after padding",
943 )
944 }
945
946 pub struct DecoderReader<R, A, const PAD: bool>
953 where
954 A: Alphabet,
955 {
956 inner: Option<R>,
957 engine: Engine<A, PAD>,
958 pending: [u8; 4],
959 pending_len: usize,
960 output: OutputQueue<3>,
961 finished: bool,
962 terminal_seen: bool,
963 }
964
965 impl<R, A, const PAD: bool> DecoderReader<R, A, PAD>
966 where
967 A: Alphabet,
968 {
969 #[must_use]
971 pub fn new(inner: R, engine: Engine<A, PAD>) -> Self {
972 Self {
973 inner: Some(inner),
974 engine,
975 pending: [0; 4],
976 pending_len: 0,
977 output: OutputQueue::new(),
978 finished: false,
979 terminal_seen: false,
980 }
981 }
982
983 #[must_use]
985 pub fn get_ref(&self) -> &R {
986 self.inner_ref()
987 }
988
989 pub fn get_mut(&mut self) -> &mut R {
991 self.inner_mut()
992 }
993
994 #[must_use]
996 pub fn into_inner(mut self) -> R {
997 self.take_inner()
998 }
999
1000 fn inner_ref(&self) -> &R {
1001 match &self.inner {
1002 Some(inner) => inner,
1003 None => unreachable!("stream decoder reader inner reader was already taken"),
1004 }
1005 }
1006
1007 fn inner_mut(&mut self) -> &mut R {
1008 match &mut self.inner {
1009 Some(inner) => inner,
1010 None => unreachable!("stream decoder reader inner reader was already taken"),
1011 }
1012 }
1013
1014 fn take_inner(&mut self) -> R {
1015 match self.inner.take() {
1016 Some(inner) => inner,
1017 None => unreachable!("stream decoder reader inner reader was already taken"),
1018 }
1019 }
1020
1021 fn clear_pending(&mut self) {
1022 crate::wipe_bytes(&mut self.pending);
1023 self.pending_len = 0;
1024 }
1025 }
1026
1027 impl<R, A, const PAD: bool> Drop for DecoderReader<R, A, PAD>
1028 where
1029 A: Alphabet,
1030 {
1031 fn drop(&mut self) {
1032 self.clear_pending();
1033 self.output.clear_all();
1034 }
1035 }
1036
1037 impl<R, A, const PAD: bool> Read for DecoderReader<R, A, PAD>
1038 where
1039 R: Read,
1040 A: Alphabet,
1041 {
1042 fn read(&mut self, output: &mut [u8]) -> io::Result<usize> {
1043 if output.is_empty() {
1044 return Ok(0);
1045 }
1046
1047 while self.output.is_empty() && !self.finished {
1048 self.fill_output()?;
1049 }
1050
1051 let mut written = 0;
1052 while written < output.len() {
1053 let Some(byte) = self.output.pop_front() else {
1054 break;
1055 };
1056 output[written] = byte;
1057 written += 1;
1058 }
1059
1060 Ok(written)
1061 }
1062 }
1063
1064 impl<R, A, const PAD: bool> DecoderReader<R, A, PAD>
1065 where
1066 R: Read,
1067 A: Alphabet,
1068 {
1069 fn fill_output(&mut self) -> io::Result<()> {
1070 if self.terminal_seen {
1071 self.finished = true;
1072 return Ok(());
1073 }
1074
1075 let mut input = [0u8; 4];
1076 let available = 4 - self.pending_len;
1077 let read = self.inner_mut().read(&mut input[..available])?;
1078 if read == 0 {
1079 crate::wipe_bytes(&mut input);
1080 self.finished = true;
1081 self.push_final_pending()?;
1082 return Ok(());
1083 }
1084
1085 self.pending[self.pending_len..self.pending_len + read].copy_from_slice(&input[..read]);
1086 crate::wipe_bytes(&mut input);
1087 self.pending_len += read;
1088 if self.pending_len < 4 {
1089 return Ok(());
1090 }
1091
1092 let mut quad = self.pending;
1093 self.clear_pending();
1094 let result = self.push_decoded(&quad);
1095 crate::wipe_bytes(&mut quad);
1096 result?;
1097 if self.terminal_seen {
1098 self.finished = true;
1099 }
1100 Ok(())
1101 }
1102
1103 fn push_final_pending(&mut self) -> io::Result<()> {
1104 if self.pending_len == 0 {
1105 return Ok(());
1106 }
1107
1108 let mut pending = [0u8; 4];
1109 pending[..self.pending_len].copy_from_slice(&self.pending[..self.pending_len]);
1110 let pending_len = self.pending_len;
1111 self.clear_pending();
1112 let result = self.push_decoded(&pending[..pending_len]);
1113 crate::wipe_bytes(&mut pending);
1114 result
1115 }
1116
1117 fn push_decoded(&mut self, input: &[u8]) -> io::Result<()> {
1118 let mut decoded = [0u8; 3];
1119 let written = match self.engine.decode_slice(input, &mut decoded) {
1120 Ok(written) => written,
1121 Err(err) => {
1122 crate::wipe_bytes(&mut decoded);
1123 return Err(decode_error_to_io(err));
1124 }
1125 };
1126 let result = self.output.push_slice(&decoded[..written]);
1127 crate::wipe_bytes(&mut decoded);
1128 result?;
1129 if input.len() == 4 && written < 3 {
1130 self.terminal_seen = true;
1131 }
1132 Ok(())
1133 }
1134 }
1135
1136 pub struct EncoderReader<R, A, const PAD: bool>
1138 where
1139 A: Alphabet,
1140 {
1141 inner: Option<R>,
1142 engine: Engine<A, PAD>,
1143 pending: [u8; 2],
1144 pending_len: usize,
1145 output: OutputQueue<1024>,
1146 finished: bool,
1147 }
1148
1149 impl<R, A, const PAD: bool> EncoderReader<R, A, PAD>
1150 where
1151 A: Alphabet,
1152 {
1153 #[must_use]
1155 pub fn new(inner: R, engine: Engine<A, PAD>) -> Self {
1156 Self {
1157 inner: Some(inner),
1158 engine,
1159 pending: [0; 2],
1160 pending_len: 0,
1161 output: OutputQueue::new(),
1162 finished: false,
1163 }
1164 }
1165
1166 #[must_use]
1168 pub fn get_ref(&self) -> &R {
1169 self.inner_ref()
1170 }
1171
1172 pub fn get_mut(&mut self) -> &mut R {
1174 self.inner_mut()
1175 }
1176
1177 #[must_use]
1179 pub fn into_inner(mut self) -> R {
1180 self.take_inner()
1181 }
1182
1183 fn inner_ref(&self) -> &R {
1184 match &self.inner {
1185 Some(inner) => inner,
1186 None => unreachable!("stream encoder reader inner reader was already taken"),
1187 }
1188 }
1189
1190 fn inner_mut(&mut self) -> &mut R {
1191 match &mut self.inner {
1192 Some(inner) => inner,
1193 None => unreachable!("stream encoder reader inner reader was already taken"),
1194 }
1195 }
1196
1197 fn take_inner(&mut self) -> R {
1198 match self.inner.take() {
1199 Some(inner) => inner,
1200 None => unreachable!("stream encoder reader inner reader was already taken"),
1201 }
1202 }
1203
1204 fn clear_pending(&mut self) {
1205 crate::wipe_bytes(&mut self.pending);
1206 self.pending_len = 0;
1207 }
1208 }
1209
1210 impl<R, A, const PAD: bool> Drop for EncoderReader<R, A, PAD>
1211 where
1212 A: Alphabet,
1213 {
1214 fn drop(&mut self) {
1215 self.clear_pending();
1216 self.output.clear_all();
1217 }
1218 }
1219
1220 impl<R, A, const PAD: bool> Read for EncoderReader<R, A, PAD>
1221 where
1222 R: Read,
1223 A: Alphabet,
1224 {
1225 fn read(&mut self, output: &mut [u8]) -> io::Result<usize> {
1226 if output.is_empty() {
1227 return Ok(0);
1228 }
1229
1230 while self.output.is_empty() && !self.finished {
1231 self.fill_output()?;
1232 }
1233
1234 let mut written = 0;
1235 while written < output.len() {
1236 let Some(byte) = self.output.pop_front() else {
1237 break;
1238 };
1239 output[written] = byte;
1240 written += 1;
1241 }
1242
1243 Ok(written)
1244 }
1245 }
1246
1247 impl<R, A, const PAD: bool> EncoderReader<R, A, PAD>
1248 where
1249 R: Read,
1250 A: Alphabet,
1251 {
1252 fn fill_output(&mut self) -> io::Result<()> {
1253 let mut input = [0u8; 768];
1254 let read = self.inner_mut().read(&mut input)?;
1255 if read == 0 {
1256 crate::wipe_bytes(&mut input);
1257 self.finished = true;
1258 self.push_final_pending()?;
1259 return Ok(());
1260 }
1261
1262 let mut consumed = 0;
1263 if self.pending_len > 0 {
1264 let needed = 3 - self.pending_len;
1265 if read < needed {
1266 self.pending[self.pending_len..self.pending_len + read]
1267 .copy_from_slice(&input[..read]);
1268 self.pending_len += read;
1269 crate::wipe_bytes(&mut input);
1270 return Ok(());
1271 }
1272
1273 let mut chunk = [0u8; 3];
1274 chunk[..self.pending_len].copy_from_slice(&self.pending[..self.pending_len]);
1275 chunk[self.pending_len..].copy_from_slice(&input[..needed]);
1276 let result = self.push_encoded(&chunk);
1277 crate::wipe_bytes(&mut chunk);
1278 if let Err(err) = result {
1279 crate::wipe_bytes(&mut input);
1280 return Err(err);
1281 }
1282 self.clear_pending();
1283 consumed += needed;
1284 }
1285
1286 let remaining = &input[consumed..read];
1287 let full_len = remaining.len() / 3 * 3;
1288 let tail_len = remaining.len() - full_len;
1289 let mut tail = [0u8; 2];
1290 tail[..tail_len].copy_from_slice(&remaining[full_len..]);
1291 let result = if full_len > 0 {
1292 self.push_encoded(&remaining[..full_len])
1293 } else {
1294 Ok(())
1295 };
1296 crate::wipe_bytes(&mut input);
1297 if let Err(err) = result {
1298 crate::wipe_bytes(&mut tail);
1299 return Err(err);
1300 }
1301 self.pending[..tail_len].copy_from_slice(&tail[..tail_len]);
1302 crate::wipe_bytes(&mut tail);
1303 self.pending_len = tail_len;
1304 Ok(())
1305 }
1306
1307 fn push_final_pending(&mut self) -> io::Result<()> {
1308 if self.pending_len == 0 {
1309 return Ok(());
1310 }
1311
1312 let mut pending = [0u8; 2];
1313 pending[..self.pending_len].copy_from_slice(&self.pending[..self.pending_len]);
1314 let pending_len = self.pending_len;
1315 self.clear_pending();
1316 let result = self.push_encoded(&pending[..pending_len]);
1317 crate::wipe_bytes(&mut pending);
1318 result
1319 }
1320
1321 fn push_encoded(&mut self, input: &[u8]) -> io::Result<()> {
1322 let mut encoded = [0u8; 1024];
1323 let written = match self.engine.encode_slice(input, &mut encoded) {
1324 Ok(written) => written,
1325 Err(err) => {
1326 crate::wipe_bytes(&mut encoded);
1327 return Err(encode_error_to_io(err));
1328 }
1329 };
1330 let result = self.output.push_slice(&encoded[..written]);
1331 crate::wipe_bytes(&mut encoded);
1332 result
1333 }
1334 }
1335}
1336
1337pub mod ct {
1345 use super::{
1346 Alphabet, DecodeError, Standard, UrlSafe, ct_decode_in_place, ct_decode_slice,
1347 ct_validate_decode,
1348 };
1349 use core::marker::PhantomData;
1350
1351 pub const STANDARD: CtEngine<Standard, true> = CtEngine::new();
1353
1354 pub const STANDARD_NO_PAD: CtEngine<Standard, false> = CtEngine::new();
1356
1357 pub const URL_SAFE: CtEngine<UrlSafe, true> = CtEngine::new();
1359
1360 pub const URL_SAFE_NO_PAD: CtEngine<UrlSafe, false> = CtEngine::new();
1362
1363 #[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
1365 pub struct CtEngine<A, const PAD: bool> {
1366 alphabet: PhantomData<A>,
1367 }
1368
1369 impl<A, const PAD: bool> CtEngine<A, PAD>
1370 where
1371 A: Alphabet,
1372 {
1373 #[must_use]
1375 pub const fn new() -> Self {
1376 Self {
1377 alphabet: PhantomData,
1378 }
1379 }
1380
1381 pub fn validate_result(&self, input: &[u8]) -> Result<(), DecodeError> {
1396 ct_validate_decode::<A, PAD>(input)
1397 }
1398
1399 #[must_use]
1413 pub fn validate(&self, input: &[u8]) -> bool {
1414 self.validate_result(input).is_ok()
1415 }
1416
1417 pub fn decode_slice(&self, input: &[u8], output: &mut [u8]) -> Result<usize, DecodeError> {
1440 ct_decode_slice::<A, PAD>(input, output)
1441 }
1442
1443 pub fn decode_slice_clear_tail(
1465 &self,
1466 input: &[u8],
1467 output: &mut [u8],
1468 ) -> Result<usize, DecodeError> {
1469 let written = match self.decode_slice(input, output) {
1470 Ok(written) => written,
1471 Err(err) => {
1472 crate::wipe_bytes(output);
1473 return Err(err);
1474 }
1475 };
1476 crate::wipe_tail(output, written);
1477 Ok(written)
1478 }
1479
1480 pub fn decode_in_place<'a>(
1497 &self,
1498 buffer: &'a mut [u8],
1499 ) -> Result<&'a mut [u8], DecodeError> {
1500 let len = ct_decode_in_place::<A, PAD>(buffer)?;
1501 Ok(&mut buffer[..len])
1502 }
1503
1504 pub fn decode_in_place_clear_tail<'a>(
1521 &self,
1522 buffer: &'a mut [u8],
1523 ) -> Result<&'a mut [u8], DecodeError> {
1524 let len = match ct_decode_in_place::<A, PAD>(buffer) {
1525 Ok(len) => len,
1526 Err(err) => {
1527 crate::wipe_bytes(buffer);
1528 return Err(err);
1529 }
1530 };
1531 crate::wipe_tail(buffer, len);
1532 Ok(&mut buffer[..len])
1533 }
1534 }
1535}
1536
1537pub const STANDARD: Engine<Standard, true> = Engine::new();
1539
1540pub const STANDARD_NO_PAD: Engine<Standard, false> = Engine::new();
1542
1543pub const URL_SAFE: Engine<UrlSafe, true> = Engine::new();
1545
1546pub const URL_SAFE_NO_PAD: Engine<UrlSafe, false> = Engine::new();
1548
1549pub const BCRYPT_NO_PAD: Engine<Bcrypt, false> = Engine::new();
1554
1555pub const CRYPT_NO_PAD: Engine<Crypt, false> = Engine::new();
1560
1561#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1563pub enum LineEnding {
1564 Lf,
1566 CrLf,
1568}
1569
1570impl LineEnding {
1571 #[must_use]
1573 pub const fn as_bytes(self) -> &'static [u8] {
1574 match self {
1575 Self::Lf => b"\n",
1576 Self::CrLf => b"\r\n",
1577 }
1578 }
1579
1580 #[must_use]
1582 pub const fn byte_len(self) -> usize {
1583 match self {
1584 Self::Lf => 1,
1585 Self::CrLf => 2,
1586 }
1587 }
1588}
1589
1590#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1596pub struct LineWrap {
1597 pub line_len: usize,
1599 pub line_ending: LineEnding,
1601}
1602
1603impl LineWrap {
1604 pub const MIME: Self = Self::new(76, LineEnding::CrLf);
1606 pub const PEM: Self = Self::new(64, LineEnding::Lf);
1608 pub const PEM_CRLF: Self = Self::new(64, LineEnding::CrLf);
1610
1611 #[must_use]
1613 pub const fn new(line_len: usize, line_ending: LineEnding) -> Self {
1614 Self {
1615 line_len,
1616 line_ending,
1617 }
1618 }
1619}
1620
1621#[allow(unsafe_code)]
1622fn wipe_bytes(bytes: &mut [u8]) {
1623 for byte in bytes {
1624 unsafe {
1628 core::ptr::write_volatile(byte, 0);
1629 }
1630 }
1631 core::sync::atomic::compiler_fence(core::sync::atomic::Ordering::SeqCst);
1632}
1633
1634fn wipe_tail(bytes: &mut [u8], start: usize) {
1635 wipe_bytes(&mut bytes[start..]);
1636}
1637
1638pub struct EncodedBuffer<const CAP: usize> {
1648 bytes: [u8; CAP],
1649 len: usize,
1650}
1651
1652impl<const CAP: usize> EncodedBuffer<CAP> {
1653 #[must_use]
1655 pub const fn new() -> Self {
1656 Self {
1657 bytes: [0u8; CAP],
1658 len: 0,
1659 }
1660 }
1661
1662 #[must_use]
1664 pub const fn len(&self) -> usize {
1665 self.len
1666 }
1667
1668 #[must_use]
1670 pub const fn is_empty(&self) -> bool {
1671 self.len == 0
1672 }
1673
1674 #[must_use]
1676 pub const fn capacity(&self) -> usize {
1677 CAP
1678 }
1679
1680 #[must_use]
1682 pub fn as_bytes(&self) -> &[u8] {
1683 &self.bytes[..self.len]
1684 }
1685
1686 #[must_use]
1693 pub fn as_str(&self) -> &str {
1694 match core::str::from_utf8(self.as_bytes()) {
1695 Ok(output) => output,
1696 Err(_) => unreachable!("base64 encoder produced non-UTF-8 output"),
1697 }
1698 }
1699
1700 pub fn clear(&mut self) {
1702 wipe_bytes(&mut self.bytes);
1703 self.len = 0;
1704 }
1705
1706 pub fn clear_tail(&mut self) {
1708 wipe_tail(&mut self.bytes, self.len);
1709 }
1710}
1711
1712impl<const CAP: usize> AsRef<[u8]> for EncodedBuffer<CAP> {
1713 fn as_ref(&self) -> &[u8] {
1714 self.as_bytes()
1715 }
1716}
1717
1718impl<const CAP: usize> Clone for EncodedBuffer<CAP> {
1719 fn clone(&self) -> Self {
1720 let mut output = Self::new();
1721 output.bytes[..self.len].copy_from_slice(self.as_bytes());
1722 output.len = self.len;
1723 output
1724 }
1725}
1726
1727impl<const CAP: usize> core::fmt::Debug for EncodedBuffer<CAP> {
1728 fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
1729 formatter
1730 .debug_struct("EncodedBuffer")
1731 .field("bytes", &"<redacted>")
1732 .field("len", &self.len)
1733 .field("capacity", &CAP)
1734 .finish()
1735 }
1736}
1737
1738impl<const CAP: usize> Default for EncodedBuffer<CAP> {
1739 fn default() -> Self {
1740 Self::new()
1741 }
1742}
1743
1744impl<const CAP: usize> Drop for EncodedBuffer<CAP> {
1745 fn drop(&mut self) {
1746 self.clear();
1747 }
1748}
1749
1750impl<const CAP: usize> Eq for EncodedBuffer<CAP> {}
1751
1752impl<const CAP: usize> PartialEq for EncodedBuffer<CAP> {
1753 fn eq(&self, other: &Self) -> bool {
1754 self.as_bytes() == other.as_bytes()
1755 }
1756}
1757
1758#[cfg(feature = "alloc")]
1769pub struct SecretBuffer {
1770 bytes: alloc::vec::Vec<u8>,
1771}
1772
1773#[cfg(feature = "alloc")]
1774impl SecretBuffer {
1775 #[must_use]
1777 pub fn from_vec(bytes: alloc::vec::Vec<u8>) -> Self {
1778 Self { bytes }
1779 }
1780
1781 #[must_use]
1783 pub fn from_slice(bytes: &[u8]) -> Self {
1784 Self {
1785 bytes: bytes.to_vec(),
1786 }
1787 }
1788
1789 #[must_use]
1791 pub fn len(&self) -> usize {
1792 self.bytes.len()
1793 }
1794
1795 #[must_use]
1797 pub fn is_empty(&self) -> bool {
1798 self.bytes.is_empty()
1799 }
1800
1801 #[must_use]
1806 pub fn expose_secret(&self) -> &[u8] {
1807 &self.bytes
1808 }
1809
1810 #[must_use]
1815 pub fn expose_secret_mut(&mut self) -> &mut [u8] {
1816 &mut self.bytes
1817 }
1818
1819 pub fn clear(&mut self) {
1821 wipe_bytes(&mut self.bytes);
1822 self.bytes.clear();
1823 }
1824}
1825
1826#[cfg(feature = "alloc")]
1827impl Clone for SecretBuffer {
1828 fn clone(&self) -> Self {
1829 Self::from_slice(self.expose_secret())
1830 }
1831}
1832
1833#[cfg(feature = "alloc")]
1834impl core::fmt::Debug for SecretBuffer {
1835 fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
1836 formatter
1837 .debug_struct("SecretBuffer")
1838 .field("bytes", &"<redacted>")
1839 .field("len", &self.len())
1840 .finish()
1841 }
1842}
1843
1844#[cfg(feature = "alloc")]
1845impl core::fmt::Display for SecretBuffer {
1846 fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
1847 formatter.write_str("<redacted>")
1848 }
1849}
1850
1851#[cfg(feature = "alloc")]
1852impl Drop for SecretBuffer {
1853 fn drop(&mut self) {
1854 wipe_bytes(&mut self.bytes);
1855 }
1856}
1857
1858#[cfg(feature = "alloc")]
1859impl Eq for SecretBuffer {}
1860
1861#[cfg(feature = "alloc")]
1862impl PartialEq for SecretBuffer {
1863 fn eq(&self, other: &Self) -> bool {
1864 self.expose_secret() == other.expose_secret()
1865 }
1866}
1867
1868#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1874pub struct Profile<A, const PAD: bool> {
1875 engine: Engine<A, PAD>,
1876 wrap: Option<LineWrap>,
1877}
1878
1879impl<A, const PAD: bool> Profile<A, PAD>
1880where
1881 A: Alphabet,
1882{
1883 #[must_use]
1885 pub const fn new(engine: Engine<A, PAD>, wrap: Option<LineWrap>) -> Self {
1886 Self { engine, wrap }
1887 }
1888
1889 #[must_use]
1891 pub const fn engine(&self) -> Engine<A, PAD> {
1892 self.engine
1893 }
1894
1895 #[must_use]
1897 pub const fn line_wrap(&self) -> Option<LineWrap> {
1898 self.wrap
1899 }
1900
1901 pub const fn encoded_len(&self, input_len: usize) -> Result<usize, EncodeError> {
1903 match self.wrap {
1904 Some(wrap) => wrapped_encoded_len(input_len, PAD, wrap),
1905 None => encoded_len(input_len, PAD),
1906 }
1907 }
1908
1909 pub fn decoded_len(&self, input: &[u8]) -> Result<usize, DecodeError> {
1911 match self.wrap {
1912 Some(wrap) => self.engine.decoded_len_wrapped(input, wrap),
1913 None => self.engine.decoded_len(input),
1914 }
1915 }
1916
1917 pub fn validate_result(&self, input: &[u8]) -> Result<(), DecodeError> {
1919 match self.wrap {
1920 Some(wrap) => self.engine.validate_wrapped_result(input, wrap),
1921 None => self.engine.validate_result(input),
1922 }
1923 }
1924
1925 #[must_use]
1927 pub fn validate(&self, input: &[u8]) -> bool {
1928 self.validate_result(input).is_ok()
1929 }
1930
1931 pub fn encode_slice(&self, input: &[u8], output: &mut [u8]) -> Result<usize, EncodeError> {
1933 match self.wrap {
1934 Some(wrap) => self.engine.encode_slice_wrapped(input, output, wrap),
1935 None => self.engine.encode_slice(input, output),
1936 }
1937 }
1938
1939 pub fn encode_slice_clear_tail(
1942 &self,
1943 input: &[u8],
1944 output: &mut [u8],
1945 ) -> Result<usize, EncodeError> {
1946 match self.wrap {
1947 Some(wrap) => self
1948 .engine
1949 .encode_slice_wrapped_clear_tail(input, output, wrap),
1950 None => self.engine.encode_slice_clear_tail(input, output),
1951 }
1952 }
1953
1954 pub fn encode_buffer<const CAP: usize>(
1960 &self,
1961 input: &[u8],
1962 ) -> Result<EncodedBuffer<CAP>, EncodeError> {
1963 let mut output = EncodedBuffer::new();
1964 let written = match self.encode_slice_clear_tail(input, &mut output.bytes) {
1965 Ok(written) => written,
1966 Err(err) => {
1967 output.clear();
1968 return Err(err);
1969 }
1970 };
1971 output.len = written;
1972 Ok(output)
1973 }
1974
1975 pub fn decode_slice(&self, input: &[u8], output: &mut [u8]) -> Result<usize, DecodeError> {
1977 match self.wrap {
1978 Some(wrap) => self.engine.decode_slice_wrapped(input, output, wrap),
1979 None => self.engine.decode_slice(input, output),
1980 }
1981 }
1982
1983 pub fn decode_slice_clear_tail(
1986 &self,
1987 input: &[u8],
1988 output: &mut [u8],
1989 ) -> Result<usize, DecodeError> {
1990 match self.wrap {
1991 Some(wrap) => self
1992 .engine
1993 .decode_slice_wrapped_clear_tail(input, output, wrap),
1994 None => self.engine.decode_slice_clear_tail(input, output),
1995 }
1996 }
1997
1998 #[cfg(feature = "alloc")]
2000 pub fn encode_vec(&self, input: &[u8]) -> Result<alloc::vec::Vec<u8>, EncodeError> {
2001 match self.wrap {
2002 Some(wrap) => self.engine.encode_wrapped_vec(input, wrap),
2003 None => self.engine.encode_vec(input),
2004 }
2005 }
2006
2007 #[cfg(feature = "alloc")]
2009 pub fn encode_secret(&self, input: &[u8]) -> Result<SecretBuffer, EncodeError> {
2010 self.encode_vec(input).map(SecretBuffer::from_vec)
2011 }
2012
2013 #[cfg(feature = "alloc")]
2015 pub fn encode_string(&self, input: &[u8]) -> Result<alloc::string::String, EncodeError> {
2016 match self.wrap {
2017 Some(wrap) => self.engine.encode_wrapped_string(input, wrap),
2018 None => self.engine.encode_string(input),
2019 }
2020 }
2021
2022 #[cfg(feature = "alloc")]
2024 pub fn decode_vec(&self, input: &[u8]) -> Result<alloc::vec::Vec<u8>, DecodeError> {
2025 match self.wrap {
2026 Some(wrap) => self.engine.decode_wrapped_vec(input, wrap),
2027 None => self.engine.decode_vec(input),
2028 }
2029 }
2030
2031 #[cfg(feature = "alloc")]
2033 pub fn decode_secret(&self, input: &[u8]) -> Result<SecretBuffer, DecodeError> {
2034 self.decode_vec(input).map(SecretBuffer::from_vec)
2035 }
2036}
2037
2038pub const MIME: Profile<Standard, true> = Profile::new(STANDARD, Some(LineWrap::MIME));
2040
2041pub const PEM: Profile<Standard, true> = Profile::new(STANDARD, Some(LineWrap::PEM));
2043
2044pub const PEM_CRLF: Profile<Standard, true> = Profile::new(STANDARD, Some(LineWrap::PEM_CRLF));
2046
2047pub const BCRYPT: Profile<Bcrypt, false> = Profile::new(BCRYPT_NO_PAD, None);
2052
2053pub const CRYPT: Profile<Crypt, false> = Profile::new(CRYPT_NO_PAD, None);
2058
2059pub const fn encoded_len(input_len: usize, padded: bool) -> Result<usize, EncodeError> {
2074 match checked_encoded_len(input_len, padded) {
2075 Some(len) => Ok(len),
2076 None => Err(EncodeError::LengthOverflow),
2077 }
2078}
2079
2080pub const fn wrapped_encoded_len(
2094 input_len: usize,
2095 padded: bool,
2096 wrap: LineWrap,
2097) -> Result<usize, EncodeError> {
2098 if wrap.line_len == 0 {
2099 return Err(EncodeError::InvalidLineWrap { line_len: 0 });
2100 }
2101
2102 let Some(encoded) = checked_encoded_len(input_len, padded) else {
2103 return Err(EncodeError::LengthOverflow);
2104 };
2105 if encoded == 0 {
2106 return Ok(0);
2107 }
2108
2109 let breaks = (encoded - 1) / wrap.line_len;
2110 let Some(line_ending_bytes) = breaks.checked_mul(wrap.line_ending.byte_len()) else {
2111 return Err(EncodeError::LengthOverflow);
2112 };
2113 match encoded.checked_add(line_ending_bytes) {
2114 Some(len) => Ok(len),
2115 None => Err(EncodeError::LengthOverflow),
2116 }
2117}
2118
2119#[must_use]
2130pub const fn checked_encoded_len(input_len: usize, padded: bool) -> Option<usize> {
2131 let groups = input_len / 3;
2132 if groups > usize::MAX / 4 {
2133 return None;
2134 }
2135 let full = groups * 4;
2136 let rem = input_len % 3;
2137 if rem == 0 {
2138 Some(full)
2139 } else if padded {
2140 full.checked_add(4)
2141 } else {
2142 full.checked_add(rem + 1)
2143 }
2144}
2145
2146#[must_use]
2157pub const fn decoded_capacity(encoded_len: usize) -> usize {
2158 let rem = encoded_len % 4;
2159 encoded_len / 4 * 3
2160 + if rem == 2 {
2161 1
2162 } else if rem == 3 {
2163 2
2164 } else {
2165 0
2166 }
2167}
2168
2169pub fn decoded_len(input: &[u8], padded: bool) -> Result<usize, DecodeError> {
2183 if padded {
2184 decoded_len_padded(input)
2185 } else {
2186 decoded_len_unpadded(input)
2187 }
2188}
2189
2190pub const fn validate_alphabet(encode: &[u8; 64]) -> Result<(), AlphabetError> {
2203 let mut index = 0;
2204 while index < encode.len() {
2205 let byte = encode[index];
2206 if !is_visible_ascii(byte) {
2207 return Err(AlphabetError::InvalidByte { index, byte });
2208 }
2209 if byte == b'=' {
2210 return Err(AlphabetError::PaddingByte { index });
2211 }
2212
2213 let mut duplicate = index + 1;
2214 while duplicate < encode.len() {
2215 if encode[duplicate] == byte {
2216 return Err(AlphabetError::DuplicateByte {
2217 first: index,
2218 second: duplicate,
2219 byte,
2220 });
2221 }
2222 duplicate += 1;
2223 }
2224
2225 index += 1;
2226 }
2227
2228 Ok(())
2229}
2230
2231#[must_use]
2257pub const fn decode_alphabet_byte(byte: u8, encode: &[u8; 64]) -> Option<u8> {
2258 let mut index = 0;
2259 let mut value = 0;
2260 while index < encode.len() {
2261 if encode[index] == byte {
2262 return Some(value);
2263 }
2264 index += 1;
2265 value += 1;
2266 }
2267 None
2268}
2269
2270pub trait Alphabet {
2272 const ENCODE: [u8; 64];
2274
2275 #[must_use]
2286 fn encode(value: u8) -> u8 {
2287 encode_alphabet_value(value, &Self::ENCODE)
2288 }
2289
2290 fn decode(byte: u8) -> Option<u8>;
2292}
2293
2294const fn is_visible_ascii(byte: u8) -> bool {
2295 byte >= 0x21 && byte <= 0x7e
2296}
2297
2298#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
2300pub struct Standard;
2301
2302impl Alphabet for Standard {
2303 const ENCODE: [u8; 64] = *b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
2304
2305 #[inline]
2306 fn encode(value: u8) -> u8 {
2307 encode_ascii_base64(value, Self::ENCODE[62], Self::ENCODE[63])
2308 }
2309
2310 #[inline]
2311 fn decode(byte: u8) -> Option<u8> {
2312 decode_ascii_base64(byte, Self::ENCODE[62], Self::ENCODE[63])
2313 }
2314}
2315
2316#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
2318pub struct UrlSafe;
2319
2320impl Alphabet for UrlSafe {
2321 const ENCODE: [u8; 64] = *b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
2322
2323 #[inline]
2324 fn encode(value: u8) -> u8 {
2325 encode_ascii_base64(value, Self::ENCODE[62], Self::ENCODE[63])
2326 }
2327
2328 #[inline]
2329 fn decode(byte: u8) -> Option<u8> {
2330 decode_ascii_base64(byte, Self::ENCODE[62], Self::ENCODE[63])
2331 }
2332}
2333
2334#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
2340pub struct Bcrypt;
2341
2342impl Alphabet for Bcrypt {
2343 const ENCODE: [u8; 64] = *b"./ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
2344
2345 #[inline]
2346 fn decode(byte: u8) -> Option<u8> {
2347 decode_alphabet_byte(byte, &Self::ENCODE)
2348 }
2349}
2350
2351#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
2356pub struct Crypt;
2357
2358impl Alphabet for Crypt {
2359 const ENCODE: [u8; 64] = *b"./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
2360
2361 #[inline]
2362 fn decode(byte: u8) -> Option<u8> {
2363 decode_alphabet_byte(byte, &Self::ENCODE)
2364 }
2365}
2366
2367#[inline]
2368const fn encode_base64_value<A: Alphabet>(value: u8) -> u8 {
2369 encode_alphabet_value(value, &A::ENCODE)
2370}
2371
2372#[inline]
2373fn encode_base64_value_runtime<A: Alphabet>(value: u8) -> u8 {
2374 A::encode(value)
2375}
2376
2377#[inline]
2378const fn encode_alphabet_value(value: u8, encode: &[u8; 64]) -> u8 {
2379 let mut output = 0;
2380 let mut index = 0;
2381 let mut candidate = 0;
2382 while index < encode.len() {
2383 output |= encode[index] & ct_mask_eq_u8(value, candidate);
2384 index += 1;
2385 candidate += 1;
2386 }
2387 output
2388}
2389
2390#[inline]
2391const fn encode_ascii_base64(value: u8, value_62_byte: u8, value_63_byte: u8) -> u8 {
2392 let upper = ct_mask_lt_u8(value, 26);
2393 let lower = ct_mask_lt_u8(value.wrapping_sub(26), 26);
2394 let digit = ct_mask_lt_u8(value.wrapping_sub(52), 10);
2395 let value_62 = ct_mask_eq_u8(value, 0x3e);
2396 let value_63 = ct_mask_eq_u8(value, 0x3f);
2397
2398 (value.wrapping_add(b'A') & upper)
2399 | (value.wrapping_sub(26).wrapping_add(b'a') & lower)
2400 | (value.wrapping_sub(52).wrapping_add(b'0') & digit)
2401 | (value_62_byte & value_62)
2402 | (value_63_byte & value_63)
2403}
2404
2405#[inline]
2406fn decode_ascii_base64(byte: u8, value_62_byte: u8, value_63_byte: u8) -> Option<u8> {
2407 let upper = ct_mask_lt_u8(byte.wrapping_sub(b'A'), 26);
2408 let lower = ct_mask_lt_u8(byte.wrapping_sub(b'a'), 26);
2409 let digit = ct_mask_lt_u8(byte.wrapping_sub(b'0'), 10);
2410 let value_62 = ct_mask_eq_u8(byte, value_62_byte);
2411 let value_63 = ct_mask_eq_u8(byte, value_63_byte);
2412 let valid = upper | lower | digit | value_62 | value_63;
2413
2414 let decoded = (byte.wrapping_sub(b'A') & upper)
2415 | (byte.wrapping_sub(b'a').wrapping_add(26) & lower)
2416 | (byte.wrapping_sub(b'0').wrapping_add(52) & digit)
2417 | (0x3e & value_62)
2418 | (0x3f & value_63);
2419
2420 if valid == 0 { None } else { Some(decoded) }
2421}
2422
2423#[inline]
2424const fn ct_mask_bit(bit: u8) -> u8 {
2425 0u8.wrapping_sub(bit & 1)
2426}
2427
2428#[inline]
2429const fn ct_mask_nonzero_u8(value: u8) -> u8 {
2430 let wide = value as u16;
2431 let negative = 0u16.wrapping_sub(wide);
2432 let nonzero = ((wide | negative) >> 8) as u8;
2433 ct_mask_bit(nonzero)
2434}
2435
2436#[inline]
2437const fn ct_mask_eq_u8(left: u8, right: u8) -> u8 {
2438 !ct_mask_nonzero_u8(left ^ right)
2439}
2440
2441#[inline]
2442const fn ct_mask_lt_u8(left: u8, right: u8) -> u8 {
2443 let diff = (left as u16).wrapping_sub(right as u16);
2444 ct_mask_bit((diff >> 8) as u8)
2445}
2446
2447mod backend {
2448 use super::{
2449 Alphabet, DecodeError, EncodeError, checked_encoded_len, decode_padded, decode_unpadded,
2450 encode_base64_value_runtime,
2451 };
2452
2453 pub(super) fn encode_slice<A, const PAD: bool>(
2454 input: &[u8],
2455 output: &mut [u8],
2456 ) -> Result<usize, EncodeError>
2457 where
2458 A: Alphabet,
2459 {
2460 #[cfg(feature = "simd")]
2461 match super::simd::active_backend() {
2462 super::simd::ActiveBackend::Scalar => {}
2463 }
2464
2465 scalar_encode_slice::<A, PAD>(input, output)
2466 }
2467
2468 pub(super) fn decode_slice<A, const PAD: bool>(
2469 input: &[u8],
2470 output: &mut [u8],
2471 ) -> Result<usize, DecodeError>
2472 where
2473 A: Alphabet,
2474 {
2475 #[cfg(feature = "simd")]
2476 match super::simd::active_backend() {
2477 super::simd::ActiveBackend::Scalar => {}
2478 }
2479
2480 scalar_decode_slice::<A, PAD>(input, output)
2481 }
2482
2483 #[cfg(test)]
2484 pub(super) fn scalar_reference_encode_slice<A, const PAD: bool>(
2485 input: &[u8],
2486 output: &mut [u8],
2487 ) -> Result<usize, EncodeError>
2488 where
2489 A: Alphabet,
2490 {
2491 scalar_encode_slice::<A, PAD>(input, output)
2492 }
2493
2494 #[cfg(test)]
2495 pub(super) fn scalar_reference_decode_slice<A, const PAD: bool>(
2496 input: &[u8],
2497 output: &mut [u8],
2498 ) -> Result<usize, DecodeError>
2499 where
2500 A: Alphabet,
2501 {
2502 scalar_decode_slice::<A, PAD>(input, output)
2503 }
2504
2505 fn scalar_encode_slice<A, const PAD: bool>(
2506 input: &[u8],
2507 output: &mut [u8],
2508 ) -> Result<usize, EncodeError>
2509 where
2510 A: Alphabet,
2511 {
2512 let required = checked_encoded_len(input.len(), PAD).ok_or(EncodeError::LengthOverflow)?;
2513 if output.len() < required {
2514 return Err(EncodeError::OutputTooSmall {
2515 required,
2516 available: output.len(),
2517 });
2518 }
2519
2520 let mut read = 0;
2521 let mut write = 0;
2522 while read + 3 <= input.len() {
2523 let b0 = input[read];
2524 let b1 = input[read + 1];
2525 let b2 = input[read + 2];
2526
2527 output[write] = encode_base64_value_runtime::<A>(b0 >> 2);
2528 output[write + 1] =
2529 encode_base64_value_runtime::<A>(((b0 & 0b0000_0011) << 4) | (b1 >> 4));
2530 output[write + 2] =
2531 encode_base64_value_runtime::<A>(((b1 & 0b0000_1111) << 2) | (b2 >> 6));
2532 output[write + 3] = encode_base64_value_runtime::<A>(b2 & 0b0011_1111);
2533
2534 read += 3;
2535 write += 4;
2536 }
2537
2538 match input.len() - read {
2539 0 => {}
2540 1 => {
2541 let b0 = input[read];
2542 output[write] = encode_base64_value_runtime::<A>(b0 >> 2);
2543 output[write + 1] = encode_base64_value_runtime::<A>((b0 & 0b0000_0011) << 4);
2544 write += 2;
2545 if PAD {
2546 output[write] = b'=';
2547 output[write + 1] = b'=';
2548 write += 2;
2549 }
2550 }
2551 2 => {
2552 let b0 = input[read];
2553 let b1 = input[read + 1];
2554 output[write] = encode_base64_value_runtime::<A>(b0 >> 2);
2555 output[write + 1] =
2556 encode_base64_value_runtime::<A>(((b0 & 0b0000_0011) << 4) | (b1 >> 4));
2557 output[write + 2] = encode_base64_value_runtime::<A>((b1 & 0b0000_1111) << 2);
2558 write += 3;
2559 if PAD {
2560 output[write] = b'=';
2561 write += 1;
2562 }
2563 }
2564 _ => unreachable!(),
2565 }
2566
2567 Ok(write)
2568 }
2569
2570 fn scalar_decode_slice<A, const PAD: bool>(
2571 input: &[u8],
2572 output: &mut [u8],
2573 ) -> Result<usize, DecodeError>
2574 where
2575 A: Alphabet,
2576 {
2577 if input.is_empty() {
2578 return Ok(0);
2579 }
2580
2581 if PAD {
2582 decode_padded::<A>(input, output)
2583 } else {
2584 decode_unpadded::<A>(input, output)
2585 }
2586 }
2587}
2588
2589pub struct Engine<A, const PAD: bool> {
2591 alphabet: core::marker::PhantomData<A>,
2592}
2593
2594impl<A, const PAD: bool> Clone for Engine<A, PAD> {
2595 fn clone(&self) -> Self {
2596 *self
2597 }
2598}
2599
2600impl<A, const PAD: bool> Copy for Engine<A, PAD> {}
2601
2602impl<A, const PAD: bool> core::fmt::Debug for Engine<A, PAD> {
2603 fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
2604 formatter
2605 .debug_struct("Engine")
2606 .field("padded", &PAD)
2607 .finish()
2608 }
2609}
2610
2611impl<A, const PAD: bool> Default for Engine<A, PAD> {
2612 fn default() -> Self {
2613 Self {
2614 alphabet: core::marker::PhantomData,
2615 }
2616 }
2617}
2618
2619impl<A, const PAD: bool> Eq for Engine<A, PAD> {}
2620
2621impl<A, const PAD: bool> PartialEq for Engine<A, PAD> {
2622 fn eq(&self, _other: &Self) -> bool {
2623 true
2624 }
2625}
2626
2627impl<A, const PAD: bool> Engine<A, PAD>
2628where
2629 A: Alphabet,
2630{
2631 #[must_use]
2633 pub const fn new() -> Self {
2634 Self {
2635 alphabet: core::marker::PhantomData,
2636 }
2637 }
2638
2639 pub const fn encoded_len(&self, input_len: usize) -> Result<usize, EncodeError> {
2641 encoded_len(input_len, PAD)
2642 }
2643
2644 #[must_use]
2646 pub const fn checked_encoded_len(&self, input_len: usize) -> Option<usize> {
2647 checked_encoded_len(input_len, PAD)
2648 }
2649
2650 pub const fn wrapped_encoded_len(
2655 &self,
2656 input_len: usize,
2657 wrap: LineWrap,
2658 ) -> Result<usize, EncodeError> {
2659 wrapped_encoded_len(input_len, PAD, wrap)
2660 }
2661
2662 pub fn decoded_len(&self, input: &[u8]) -> Result<usize, DecodeError> {
2667 decoded_len(input, PAD)
2668 }
2669
2670 pub fn decoded_len_legacy(&self, input: &[u8]) -> Result<usize, DecodeError> {
2676 validate_legacy_decode::<A, PAD>(input)
2677 }
2678
2679 pub fn decoded_len_wrapped(&self, input: &[u8], wrap: LineWrap) -> Result<usize, DecodeError> {
2686 validate_wrapped_decode::<A, PAD>(input, wrap)
2687 }
2688
2689 pub fn validate_result(&self, input: &[u8]) -> Result<(), DecodeError> {
2704 validate_decode::<A, PAD>(input).map(|_| ())
2705 }
2706
2707 #[must_use]
2720 pub fn validate(&self, input: &[u8]) -> bool {
2721 self.validate_result(input).is_ok()
2722 }
2723
2724 pub fn validate_legacy_result(&self, input: &[u8]) -> Result<(), DecodeError> {
2739 validate_legacy_decode::<A, PAD>(input).map(|_| ())
2740 }
2741
2742 #[must_use]
2756 pub fn validate_legacy(&self, input: &[u8]) -> bool {
2757 self.validate_legacy_result(input).is_ok()
2758 }
2759
2760 pub fn validate_wrapped_result(&self, input: &[u8], wrap: LineWrap) -> Result<(), DecodeError> {
2776 validate_wrapped_decode::<A, PAD>(input, wrap).map(|_| ())
2777 }
2778
2779 #[must_use]
2793 pub fn validate_wrapped(&self, input: &[u8], wrap: LineWrap) -> bool {
2794 self.validate_wrapped_result(input, wrap).is_ok()
2795 }
2796
2797 #[must_use]
2829 pub const fn encode_array<const INPUT_LEN: usize, const OUTPUT_LEN: usize>(
2830 &self,
2831 input: &[u8; INPUT_LEN],
2832 ) -> [u8; OUTPUT_LEN] {
2833 let Some(required) = checked_encoded_len(INPUT_LEN, PAD) else {
2834 panic!("encoded base64 length overflows usize");
2835 };
2836 assert!(
2837 required == OUTPUT_LEN,
2838 "base64 output array has incorrect length"
2839 );
2840
2841 let mut output = [0u8; OUTPUT_LEN];
2842 let mut read = 0;
2843 let mut write = 0;
2844 while INPUT_LEN - read >= 3 {
2845 let b0 = input[read];
2846 let b1 = input[read + 1];
2847 let b2 = input[read + 2];
2848
2849 output[write] = encode_base64_value::<A>(b0 >> 2);
2850 output[write + 1] = encode_base64_value::<A>(((b0 & 0b0000_0011) << 4) | (b1 >> 4));
2851 output[write + 2] = encode_base64_value::<A>(((b1 & 0b0000_1111) << 2) | (b2 >> 6));
2852 output[write + 3] = encode_base64_value::<A>(b2 & 0b0011_1111);
2853
2854 read += 3;
2855 write += 4;
2856 }
2857
2858 match INPUT_LEN - read {
2859 0 => {}
2860 1 => {
2861 let b0 = input[read];
2862 output[write] = encode_base64_value::<A>(b0 >> 2);
2863 output[write + 1] = encode_base64_value::<A>((b0 & 0b0000_0011) << 4);
2864 write += 2;
2865 if PAD {
2866 output[write] = b'=';
2867 output[write + 1] = b'=';
2868 }
2869 }
2870 2 => {
2871 let b0 = input[read];
2872 let b1 = input[read + 1];
2873 output[write] = encode_base64_value::<A>(b0 >> 2);
2874 output[write + 1] = encode_base64_value::<A>(((b0 & 0b0000_0011) << 4) | (b1 >> 4));
2875 output[write + 2] = encode_base64_value::<A>((b1 & 0b0000_1111) << 2);
2876 if PAD {
2877 output[write + 3] = b'=';
2878 }
2879 }
2880 _ => unreachable!(),
2881 }
2882
2883 output
2884 }
2885
2886 pub fn encode_slice(&self, input: &[u8], output: &mut [u8]) -> Result<usize, EncodeError> {
2888 backend::encode_slice::<A, PAD>(input, output)
2889 }
2890
2891 pub fn encode_slice_wrapped(
2910 &self,
2911 input: &[u8],
2912 output: &mut [u8],
2913 wrap: LineWrap,
2914 ) -> Result<usize, EncodeError> {
2915 let required = self.wrapped_encoded_len(input.len(), wrap)?;
2916 if output.len() < required {
2917 return Err(EncodeError::OutputTooSmall {
2918 required,
2919 available: output.len(),
2920 });
2921 }
2922
2923 let encoded_len =
2924 checked_encoded_len(input.len(), PAD).ok_or(EncodeError::LengthOverflow)?;
2925 if encoded_len == 0 {
2926 return Ok(0);
2927 }
2928
2929 if output.len() < required.saturating_add(encoded_len) {
2930 let mut scratch = [0u8; 1024];
2931 let mut input_offset = 0;
2932 let mut output_offset = 0;
2933 let mut column = 0;
2934
2935 while input_offset < input.len() {
2936 let remaining = input.len() - input_offset;
2937 let mut take = remaining.min(768);
2938 if remaining > take {
2939 take -= take % 3;
2940 }
2941 if take == 0 {
2942 take = remaining;
2943 }
2944
2945 let encoded =
2946 self.encode_slice(&input[input_offset..input_offset + take], &mut scratch)?;
2947 write_wrapped_bytes(
2948 &scratch[..encoded],
2949 output,
2950 &mut output_offset,
2951 &mut column,
2952 wrap,
2953 );
2954 wipe_bytes(&mut scratch[..encoded]);
2955 input_offset += take;
2956 }
2957
2958 Ok(output_offset)
2959 } else {
2960 let encoded =
2961 self.encode_slice(input, &mut output[required..required + encoded_len])?;
2962 let mut output_offset = 0;
2963 let mut column = 0;
2964 let mut read = required;
2965 while read < required + encoded {
2966 let byte = output[read];
2967 write_wrapped_byte(byte, output, &mut output_offset, &mut column, wrap);
2968 read += 1;
2969 }
2970 wipe_bytes(&mut output[required..required + encoded]);
2971 Ok(output_offset)
2972 }
2973 }
2974
2975 pub fn encode_slice_wrapped_clear_tail(
2981 &self,
2982 input: &[u8],
2983 output: &mut [u8],
2984 wrap: LineWrap,
2985 ) -> Result<usize, EncodeError> {
2986 let written = match self.encode_slice_wrapped(input, output, wrap) {
2987 Ok(written) => written,
2988 Err(err) => {
2989 wipe_bytes(output);
2990 return Err(err);
2991 }
2992 };
2993 wipe_tail(output, written);
2994 Ok(written)
2995 }
2996
2997 #[cfg(feature = "alloc")]
2999 pub fn encode_wrapped_vec(
3000 &self,
3001 input: &[u8],
3002 wrap: LineWrap,
3003 ) -> Result<alloc::vec::Vec<u8>, EncodeError> {
3004 let required = self.wrapped_encoded_len(input.len(), wrap)?;
3005 let mut output = alloc::vec![0; required];
3006 let written = self.encode_slice_wrapped(input, &mut output, wrap)?;
3007 output.truncate(written);
3008 Ok(output)
3009 }
3010
3011 #[cfg(feature = "alloc")]
3013 pub fn encode_wrapped_string(
3014 &self,
3015 input: &[u8],
3016 wrap: LineWrap,
3017 ) -> Result<alloc::string::String, EncodeError> {
3018 let output = self.encode_wrapped_vec(input, wrap)?;
3019 match alloc::string::String::from_utf8(output) {
3020 Ok(output) => Ok(output),
3021 Err(_) => unreachable!("base64 encoder produced non-UTF-8 output"),
3022 }
3023 }
3024
3025 pub fn encode_slice_clear_tail(
3045 &self,
3046 input: &[u8],
3047 output: &mut [u8],
3048 ) -> Result<usize, EncodeError> {
3049 let written = match self.encode_slice(input, output) {
3050 Ok(written) => written,
3051 Err(err) => {
3052 wipe_bytes(output);
3053 return Err(err);
3054 }
3055 };
3056 wipe_tail(output, written);
3057 Ok(written)
3058 }
3059
3060 pub fn encode_buffer<const CAP: usize>(
3075 &self,
3076 input: &[u8],
3077 ) -> Result<EncodedBuffer<CAP>, EncodeError> {
3078 let mut output = EncodedBuffer::new();
3079 let written = match self.encode_slice_clear_tail(input, &mut output.bytes) {
3080 Ok(written) => written,
3081 Err(err) => {
3082 output.clear();
3083 return Err(err);
3084 }
3085 };
3086 output.len = written;
3087 Ok(output)
3088 }
3089
3090 #[cfg(feature = "alloc")]
3092 pub fn encode_vec(&self, input: &[u8]) -> Result<alloc::vec::Vec<u8>, EncodeError> {
3093 let required = checked_encoded_len(input.len(), PAD).ok_or(EncodeError::LengthOverflow)?;
3094 let mut output = alloc::vec![0; required];
3095 let written = self.encode_slice(input, &mut output)?;
3096 output.truncate(written);
3097 Ok(output)
3098 }
3099
3100 #[cfg(feature = "alloc")]
3105 pub fn encode_secret(&self, input: &[u8]) -> Result<SecretBuffer, EncodeError> {
3106 self.encode_vec(input).map(SecretBuffer::from_vec)
3107 }
3108
3109 #[cfg(feature = "alloc")]
3124 pub fn encode_string(&self, input: &[u8]) -> Result<alloc::string::String, EncodeError> {
3125 let output = self.encode_vec(input)?;
3126 match alloc::string::String::from_utf8(output) {
3127 Ok(output) => Ok(output),
3128 Err(_) => unreachable!("base64 encoder produced non-UTF-8 output"),
3129 }
3130 }
3131
3132 pub fn encode_in_place<'a>(
3149 &self,
3150 buffer: &'a mut [u8],
3151 input_len: usize,
3152 ) -> Result<&'a mut [u8], EncodeError> {
3153 if input_len > buffer.len() {
3154 return Err(EncodeError::InputTooLarge {
3155 input_len,
3156 buffer_len: buffer.len(),
3157 });
3158 }
3159
3160 let required = checked_encoded_len(input_len, PAD).ok_or(EncodeError::LengthOverflow)?;
3161 if buffer.len() < required {
3162 return Err(EncodeError::OutputTooSmall {
3163 required,
3164 available: buffer.len(),
3165 });
3166 }
3167
3168 let mut read = input_len;
3169 let mut write = required;
3170
3171 match input_len % 3 {
3172 0 => {}
3173 1 => {
3174 read -= 1;
3175 let b0 = buffer[read];
3176 if PAD {
3177 write -= 4;
3178 buffer[write] = encode_base64_value_runtime::<A>(b0 >> 2);
3179 buffer[write + 1] = encode_base64_value_runtime::<A>((b0 & 0b0000_0011) << 4);
3180 buffer[write + 2] = b'=';
3181 buffer[write + 3] = b'=';
3182 } else {
3183 write -= 2;
3184 buffer[write] = encode_base64_value_runtime::<A>(b0 >> 2);
3185 buffer[write + 1] = encode_base64_value_runtime::<A>((b0 & 0b0000_0011) << 4);
3186 }
3187 }
3188 2 => {
3189 read -= 2;
3190 let b0 = buffer[read];
3191 let b1 = buffer[read + 1];
3192 if PAD {
3193 write -= 4;
3194 buffer[write] = encode_base64_value_runtime::<A>(b0 >> 2);
3195 buffer[write + 1] =
3196 encode_base64_value_runtime::<A>(((b0 & 0b0000_0011) << 4) | (b1 >> 4));
3197 buffer[write + 2] = encode_base64_value_runtime::<A>((b1 & 0b0000_1111) << 2);
3198 buffer[write + 3] = b'=';
3199 } else {
3200 write -= 3;
3201 buffer[write] = encode_base64_value_runtime::<A>(b0 >> 2);
3202 buffer[write + 1] =
3203 encode_base64_value_runtime::<A>(((b0 & 0b0000_0011) << 4) | (b1 >> 4));
3204 buffer[write + 2] = encode_base64_value_runtime::<A>((b1 & 0b0000_1111) << 2);
3205 }
3206 }
3207 _ => unreachable!(),
3208 }
3209
3210 while read > 0 {
3211 read -= 3;
3212 write -= 4;
3213 let b0 = buffer[read];
3214 let b1 = buffer[read + 1];
3215 let b2 = buffer[read + 2];
3216
3217 buffer[write] = encode_base64_value_runtime::<A>(b0 >> 2);
3218 buffer[write + 1] =
3219 encode_base64_value_runtime::<A>(((b0 & 0b0000_0011) << 4) | (b1 >> 4));
3220 buffer[write + 2] =
3221 encode_base64_value_runtime::<A>(((b1 & 0b0000_1111) << 2) | (b2 >> 6));
3222 buffer[write + 3] = encode_base64_value_runtime::<A>(b2 & 0b0011_1111);
3223 }
3224
3225 debug_assert_eq!(write, 0);
3226 Ok(&mut buffer[..required])
3227 }
3228
3229 pub fn encode_in_place_clear_tail<'a>(
3247 &self,
3248 buffer: &'a mut [u8],
3249 input_len: usize,
3250 ) -> Result<&'a mut [u8], EncodeError> {
3251 let len = match self.encode_in_place(buffer, input_len) {
3252 Ok(encoded) => encoded.len(),
3253 Err(err) => {
3254 wipe_bytes(buffer);
3255 return Err(err);
3256 }
3257 };
3258 wipe_tail(buffer, len);
3259 Ok(&mut buffer[..len])
3260 }
3261
3262 pub fn decode_slice(&self, input: &[u8], output: &mut [u8]) -> Result<usize, DecodeError> {
3267 backend::decode_slice::<A, PAD>(input, output)
3268 }
3269
3270 pub fn decode_slice_clear_tail(
3290 &self,
3291 input: &[u8],
3292 output: &mut [u8],
3293 ) -> Result<usize, DecodeError> {
3294 let written = match self.decode_slice(input, output) {
3295 Ok(written) => written,
3296 Err(err) => {
3297 wipe_bytes(output);
3298 return Err(err);
3299 }
3300 };
3301 wipe_tail(output, written);
3302 Ok(written)
3303 }
3304
3305 pub fn decode_slice_legacy(
3311 &self,
3312 input: &[u8],
3313 output: &mut [u8],
3314 ) -> Result<usize, DecodeError> {
3315 let required = validate_legacy_decode::<A, PAD>(input)?;
3316 if output.len() < required {
3317 return Err(DecodeError::OutputTooSmall {
3318 required,
3319 available: output.len(),
3320 });
3321 }
3322 decode_legacy_to_slice::<A, PAD>(input, output)
3323 }
3324
3325 pub fn decode_slice_legacy_clear_tail(
3345 &self,
3346 input: &[u8],
3347 output: &mut [u8],
3348 ) -> Result<usize, DecodeError> {
3349 let written = match self.decode_slice_legacy(input, output) {
3350 Ok(written) => written,
3351 Err(err) => {
3352 wipe_bytes(output);
3353 return Err(err);
3354 }
3355 };
3356 wipe_tail(output, written);
3357 Ok(written)
3358 }
3359
3360 pub fn decode_slice_wrapped(
3367 &self,
3368 input: &[u8],
3369 output: &mut [u8],
3370 wrap: LineWrap,
3371 ) -> Result<usize, DecodeError> {
3372 let required = validate_wrapped_decode::<A, PAD>(input, wrap)?;
3373 if output.len() < required {
3374 return Err(DecodeError::OutputTooSmall {
3375 required,
3376 available: output.len(),
3377 });
3378 }
3379 decode_wrapped_to_slice::<A, PAD>(input, output, wrap)
3380 }
3381
3382 pub fn decode_slice_wrapped_clear_tail(
3388 &self,
3389 input: &[u8],
3390 output: &mut [u8],
3391 wrap: LineWrap,
3392 ) -> Result<usize, DecodeError> {
3393 let written = match self.decode_slice_wrapped(input, output, wrap) {
3394 Ok(written) => written,
3395 Err(err) => {
3396 wipe_bytes(output);
3397 return Err(err);
3398 }
3399 };
3400 wipe_tail(output, written);
3401 Ok(written)
3402 }
3403
3404 #[cfg(feature = "alloc")]
3408 pub fn decode_vec(&self, input: &[u8]) -> Result<alloc::vec::Vec<u8>, DecodeError> {
3409 let required = validate_decode::<A, PAD>(input)?;
3410 let mut output = alloc::vec![0; required];
3411 let written = match self.decode_slice(input, &mut output) {
3412 Ok(written) => written,
3413 Err(err) => {
3414 wipe_bytes(&mut output);
3415 return Err(err);
3416 }
3417 };
3418 output.truncate(written);
3419 Ok(output)
3420 }
3421
3422 #[cfg(feature = "alloc")]
3427 pub fn decode_secret(&self, input: &[u8]) -> Result<SecretBuffer, DecodeError> {
3428 self.decode_vec(input).map(SecretBuffer::from_vec)
3429 }
3430
3431 #[cfg(feature = "alloc")]
3434 pub fn decode_vec_legacy(&self, input: &[u8]) -> Result<alloc::vec::Vec<u8>, DecodeError> {
3435 let required = validate_legacy_decode::<A, PAD>(input)?;
3436 let mut output = alloc::vec![0; required];
3437 let written = match self.decode_slice_legacy(input, &mut output) {
3438 Ok(written) => written,
3439 Err(err) => {
3440 wipe_bytes(&mut output);
3441 return Err(err);
3442 }
3443 };
3444 output.truncate(written);
3445 Ok(output)
3446 }
3447
3448 #[cfg(feature = "alloc")]
3450 pub fn decode_wrapped_vec(
3451 &self,
3452 input: &[u8],
3453 wrap: LineWrap,
3454 ) -> Result<alloc::vec::Vec<u8>, DecodeError> {
3455 let required = validate_wrapped_decode::<A, PAD>(input, wrap)?;
3456 let mut output = alloc::vec![0; required];
3457 let written = match self.decode_slice_wrapped(input, &mut output, wrap) {
3458 Ok(written) => written,
3459 Err(err) => {
3460 wipe_bytes(&mut output);
3461 return Err(err);
3462 }
3463 };
3464 output.truncate(written);
3465 Ok(output)
3466 }
3467
3468 pub fn decode_in_place<'a>(&self, buffer: &'a mut [u8]) -> Result<&'a mut [u8], DecodeError> {
3480 let len = Self::decode_slice_to_start(buffer)?;
3481 Ok(&mut buffer[..len])
3482 }
3483
3484 pub fn decode_in_place_clear_tail<'a>(
3501 &self,
3502 buffer: &'a mut [u8],
3503 ) -> Result<&'a mut [u8], DecodeError> {
3504 let len = match Self::decode_slice_to_start(buffer) {
3505 Ok(len) => len,
3506 Err(err) => {
3507 wipe_bytes(buffer);
3508 return Err(err);
3509 }
3510 };
3511 wipe_tail(buffer, len);
3512 Ok(&mut buffer[..len])
3513 }
3514
3515 pub fn decode_in_place_legacy<'a>(
3520 &self,
3521 buffer: &'a mut [u8],
3522 ) -> Result<&'a mut [u8], DecodeError> {
3523 let _required = validate_legacy_decode::<A, PAD>(buffer)?;
3524 let mut write = 0;
3525 let mut read = 0;
3526 while read < buffer.len() {
3527 let byte = buffer[read];
3528 if !is_legacy_whitespace(byte) {
3529 buffer[write] = byte;
3530 write += 1;
3531 }
3532 read += 1;
3533 }
3534 let len = Self::decode_slice_to_start(&mut buffer[..write])?;
3535 Ok(&mut buffer[..len])
3536 }
3537
3538 pub fn decode_in_place_legacy_clear_tail<'a>(
3544 &self,
3545 buffer: &'a mut [u8],
3546 ) -> Result<&'a mut [u8], DecodeError> {
3547 if let Err(err) = validate_legacy_decode::<A, PAD>(buffer) {
3548 wipe_bytes(buffer);
3549 return Err(err);
3550 }
3551
3552 let mut write = 0;
3553 let mut read = 0;
3554 while read < buffer.len() {
3555 let byte = buffer[read];
3556 if !is_legacy_whitespace(byte) {
3557 buffer[write] = byte;
3558 write += 1;
3559 }
3560 read += 1;
3561 }
3562
3563 let len = match Self::decode_slice_to_start(&mut buffer[..write]) {
3564 Ok(len) => len,
3565 Err(err) => {
3566 wipe_bytes(buffer);
3567 return Err(err);
3568 }
3569 };
3570 wipe_tail(buffer, len);
3571 Ok(&mut buffer[..len])
3572 }
3573
3574 fn decode_slice_to_start(buffer: &mut [u8]) -> Result<usize, DecodeError> {
3575 let input_len = buffer.len();
3576 let mut read = 0;
3577 let mut write = 0;
3578 while read + 4 <= input_len {
3579 let chunk = read_quad(buffer, read)?;
3580 let available = buffer.len();
3581 let output_tail = buffer.get_mut(write..).ok_or(DecodeError::OutputTooSmall {
3582 required: write,
3583 available,
3584 })?;
3585 let written = decode_chunk::<A, PAD>(chunk, output_tail)
3586 .map_err(|err| err.with_index_offset(read))?;
3587 read += 4;
3588 write += written;
3589 if written < 3 {
3590 if read != input_len {
3591 return Err(DecodeError::InvalidPadding { index: read - 4 });
3592 }
3593 return Ok(write);
3594 }
3595 }
3596
3597 let rem = input_len - read;
3598 if rem == 0 {
3599 return Ok(write);
3600 }
3601 if PAD {
3602 return Err(DecodeError::InvalidLength);
3603 }
3604 let mut tail = [0u8; 3];
3605 tail[..rem].copy_from_slice(&buffer[read..input_len]);
3606 decode_tail_unpadded::<A>(&tail[..rem], &mut buffer[write..])
3607 .map_err(|err| err.with_index_offset(read))
3608 .map(|n| write + n)
3609 }
3610}
3611
3612fn write_wrapped_bytes(
3613 input: &[u8],
3614 output: &mut [u8],
3615 output_offset: &mut usize,
3616 column: &mut usize,
3617 wrap: LineWrap,
3618) {
3619 for byte in input {
3620 write_wrapped_byte(*byte, output, output_offset, column, wrap);
3621 }
3622}
3623
3624fn write_wrapped_byte(
3625 byte: u8,
3626 output: &mut [u8],
3627 output_offset: &mut usize,
3628 column: &mut usize,
3629 wrap: LineWrap,
3630) {
3631 if *column == wrap.line_len {
3632 let line_ending = wrap.line_ending.as_bytes();
3633 let mut index = 0;
3634 while index < line_ending.len() {
3635 output[*output_offset] = line_ending[index];
3636 *output_offset += 1;
3637 index += 1;
3638 }
3639 *column = 0;
3640 }
3641
3642 output[*output_offset] = byte;
3643 *output_offset += 1;
3644 *column += 1;
3645}
3646
3647#[derive(Clone, Copy, Debug, Eq, PartialEq)]
3649pub enum EncodeError {
3650 LengthOverflow,
3652 InvalidLineWrap {
3654 line_len: usize,
3656 },
3657 InputTooLarge {
3659 input_len: usize,
3661 buffer_len: usize,
3663 },
3664 OutputTooSmall {
3666 required: usize,
3668 available: usize,
3670 },
3671}
3672
3673impl core::fmt::Display for EncodeError {
3674 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
3675 match self {
3676 Self::LengthOverflow => f.write_str("base64 output length overflows usize"),
3677 Self::InvalidLineWrap { line_len } => {
3678 write!(f, "base64 line wrap length {line_len} is invalid")
3679 }
3680 Self::InputTooLarge {
3681 input_len,
3682 buffer_len,
3683 } => write!(
3684 f,
3685 "base64 input length {input_len} exceeds buffer length {buffer_len}"
3686 ),
3687 Self::OutputTooSmall {
3688 required,
3689 available,
3690 } => write!(
3691 f,
3692 "base64 output buffer too small: required {required}, available {available}"
3693 ),
3694 }
3695 }
3696}
3697
3698#[cfg(feature = "std")]
3699impl std::error::Error for EncodeError {}
3700
3701#[derive(Clone, Copy, Debug, Eq, PartialEq)]
3703pub enum AlphabetError {
3704 InvalidByte {
3706 index: usize,
3708 byte: u8,
3710 },
3711 PaddingByte {
3713 index: usize,
3715 },
3716 DuplicateByte {
3718 first: usize,
3720 second: usize,
3722 byte: u8,
3724 },
3725}
3726
3727impl core::fmt::Display for AlphabetError {
3728 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
3729 match self {
3730 Self::InvalidByte { index, byte } => {
3731 write!(
3732 f,
3733 "invalid base64 alphabet byte 0x{byte:02x} at index {index}"
3734 )
3735 }
3736 Self::PaddingByte { index } => {
3737 write!(f, "base64 alphabet contains padding byte at index {index}")
3738 }
3739 Self::DuplicateByte {
3740 first,
3741 second,
3742 byte,
3743 } => write!(
3744 f,
3745 "base64 alphabet byte 0x{byte:02x} is duplicated at indexes {first} and {second}"
3746 ),
3747 }
3748 }
3749}
3750
3751#[cfg(feature = "std")]
3752impl std::error::Error for AlphabetError {}
3753
3754#[derive(Clone, Copy, Debug, Eq, PartialEq)]
3756pub enum DecodeError {
3757 InvalidInput,
3760 InvalidLength,
3762 InvalidByte {
3764 index: usize,
3766 byte: u8,
3768 },
3769 InvalidPadding {
3771 index: usize,
3773 },
3774 InvalidLineWrap {
3776 index: usize,
3778 },
3779 OutputTooSmall {
3781 required: usize,
3783 available: usize,
3785 },
3786}
3787
3788impl core::fmt::Display for DecodeError {
3789 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
3790 match self {
3791 Self::InvalidInput => f.write_str("malformed base64 input"),
3792 Self::InvalidLength => f.write_str("invalid base64 input length"),
3793 Self::InvalidByte { index, byte } => {
3794 write!(f, "invalid base64 byte 0x{byte:02x} at index {index}")
3795 }
3796 Self::InvalidPadding { index } => write!(f, "invalid base64 padding at index {index}"),
3797 Self::InvalidLineWrap { index } => {
3798 write!(f, "invalid base64 line wrapping at index {index}")
3799 }
3800 Self::OutputTooSmall {
3801 required,
3802 available,
3803 } => write!(
3804 f,
3805 "base64 decode output buffer too small: required {required}, available {available}"
3806 ),
3807 }
3808 }
3809}
3810
3811impl DecodeError {
3812 fn with_index_offset(self, offset: usize) -> Self {
3813 match self {
3814 Self::InvalidByte { index, byte } => Self::InvalidByte {
3815 index: index + offset,
3816 byte,
3817 },
3818 Self::InvalidPadding { index } => Self::InvalidPadding {
3819 index: index + offset,
3820 },
3821 Self::InvalidLineWrap { index } => Self::InvalidLineWrap {
3822 index: index + offset,
3823 },
3824 Self::InvalidInput | Self::InvalidLength | Self::OutputTooSmall { .. } => self,
3825 }
3826 }
3827}
3828
3829#[cfg(feature = "std")]
3830impl std::error::Error for DecodeError {}
3831
3832fn validate_legacy_decode<A: Alphabet, const PAD: bool>(
3833 input: &[u8],
3834) -> Result<usize, DecodeError> {
3835 let mut chunk = [0u8; 4];
3836 let mut indexes = [0usize; 4];
3837 let mut chunk_len = 0;
3838 let mut required = 0;
3839 let mut terminal_seen = false;
3840
3841 for (index, byte) in input.iter().copied().enumerate() {
3842 if is_legacy_whitespace(byte) {
3843 continue;
3844 }
3845 if terminal_seen {
3846 return Err(DecodeError::InvalidPadding { index });
3847 }
3848
3849 chunk[chunk_len] = byte;
3850 indexes[chunk_len] = index;
3851 chunk_len += 1;
3852
3853 if chunk_len == 4 {
3854 let written =
3855 validate_chunk::<A, PAD>(chunk).map_err(|err| map_chunk_error(err, &indexes))?;
3856 required += written;
3857 terminal_seen = written < 3;
3858 chunk_len = 0;
3859 }
3860 }
3861
3862 if chunk_len == 0 {
3863 return Ok(required);
3864 }
3865 if PAD {
3866 return Err(DecodeError::InvalidLength);
3867 }
3868
3869 validate_tail_unpadded::<A>(&chunk[..chunk_len])
3870 .map_err(|err| map_partial_chunk_error(err, &indexes, chunk_len))?;
3871 Ok(required + decoded_capacity(chunk_len))
3872}
3873
3874fn decode_legacy_to_slice<A: Alphabet, const PAD: bool>(
3875 input: &[u8],
3876 output: &mut [u8],
3877) -> Result<usize, DecodeError> {
3878 let mut chunk = [0u8; 4];
3879 let mut indexes = [0usize; 4];
3880 let mut chunk_len = 0;
3881 let mut write = 0;
3882 let mut terminal_seen = false;
3883
3884 for (index, byte) in input.iter().copied().enumerate() {
3885 if is_legacy_whitespace(byte) {
3886 continue;
3887 }
3888 if terminal_seen {
3889 return Err(DecodeError::InvalidPadding { index });
3890 }
3891
3892 chunk[chunk_len] = byte;
3893 indexes[chunk_len] = index;
3894 chunk_len += 1;
3895
3896 if chunk_len == 4 {
3897 let available = output.len();
3898 let output_tail = output.get_mut(write..).ok_or(DecodeError::OutputTooSmall {
3899 required: write,
3900 available,
3901 })?;
3902 let written = decode_chunk::<A, PAD>(chunk, output_tail)
3903 .map_err(|err| map_chunk_error(err, &indexes))?;
3904 write += written;
3905 terminal_seen = written < 3;
3906 chunk_len = 0;
3907 }
3908 }
3909
3910 if chunk_len == 0 {
3911 return Ok(write);
3912 }
3913 if PAD {
3914 return Err(DecodeError::InvalidLength);
3915 }
3916
3917 decode_tail_unpadded::<A>(&chunk[..chunk_len], &mut output[write..])
3918 .map_err(|err| map_partial_chunk_error(err, &indexes, chunk_len))
3919 .map(|n| write + n)
3920}
3921
3922struct WrappedBytes<'a> {
3923 input: &'a [u8],
3924 wrap: LineWrap,
3925 index: usize,
3926 line_len: usize,
3927}
3928
3929impl<'a> WrappedBytes<'a> {
3930 const fn new(input: &'a [u8], wrap: LineWrap) -> Result<Self, DecodeError> {
3931 if wrap.line_len == 0 {
3932 return Err(DecodeError::InvalidLineWrap { index: 0 });
3933 }
3934 Ok(Self {
3935 input,
3936 wrap,
3937 index: 0,
3938 line_len: 0,
3939 })
3940 }
3941
3942 fn next_byte(&mut self) -> Result<Option<(usize, u8)>, DecodeError> {
3943 loop {
3944 if self.index == self.input.len() {
3945 return Ok(None);
3946 }
3947
3948 if self.starts_with_line_ending() {
3949 let line_end_index = self.index;
3950 if self.line_len == 0 {
3951 return Err(DecodeError::InvalidLineWrap {
3952 index: line_end_index,
3953 });
3954 }
3955
3956 self.index += self.wrap.line_ending.byte_len();
3957 if self.index == self.input.len() {
3958 self.line_len = 0;
3959 return Ok(None);
3960 }
3961
3962 if self.line_len != self.wrap.line_len {
3963 return Err(DecodeError::InvalidLineWrap {
3964 index: line_end_index,
3965 });
3966 }
3967 self.line_len = 0;
3968 continue;
3969 }
3970
3971 let byte = self.input[self.index];
3972 if matches!(byte, b'\r' | b'\n') {
3973 return Err(DecodeError::InvalidLineWrap { index: self.index });
3974 }
3975
3976 self.line_len += 1;
3977 if self.line_len > self.wrap.line_len {
3978 return Err(DecodeError::InvalidLineWrap { index: self.index });
3979 }
3980
3981 let index = self.index;
3982 self.index += 1;
3983 return Ok(Some((index, byte)));
3984 }
3985 }
3986
3987 fn starts_with_line_ending(&self) -> bool {
3988 let line_ending = self.wrap.line_ending.as_bytes();
3989 let end = self.index + line_ending.len();
3990 end <= self.input.len() && &self.input[self.index..end] == line_ending
3991 }
3992}
3993
3994fn validate_wrapped_decode<A: Alphabet, const PAD: bool>(
3995 input: &[u8],
3996 wrap: LineWrap,
3997) -> Result<usize, DecodeError> {
3998 let mut bytes = WrappedBytes::new(input, wrap)?;
3999 let mut chunk = [0u8; 4];
4000 let mut indexes = [0usize; 4];
4001 let mut chunk_len = 0;
4002 let mut required = 0;
4003 let mut terminal_seen = false;
4004
4005 while let Some((index, byte)) = bytes.next_byte()? {
4006 if terminal_seen {
4007 return Err(DecodeError::InvalidPadding { index });
4008 }
4009
4010 chunk[chunk_len] = byte;
4011 indexes[chunk_len] = index;
4012 chunk_len += 1;
4013
4014 if chunk_len == 4 {
4015 let written =
4016 validate_chunk::<A, PAD>(chunk).map_err(|err| map_chunk_error(err, &indexes))?;
4017 required += written;
4018 terminal_seen = written < 3;
4019 chunk_len = 0;
4020 }
4021 }
4022
4023 if chunk_len == 0 {
4024 return Ok(required);
4025 }
4026 if PAD {
4027 return Err(DecodeError::InvalidLength);
4028 }
4029
4030 validate_tail_unpadded::<A>(&chunk[..chunk_len])
4031 .map_err(|err| map_partial_chunk_error(err, &indexes, chunk_len))?;
4032 Ok(required + decoded_capacity(chunk_len))
4033}
4034
4035fn decode_wrapped_to_slice<A: Alphabet, const PAD: bool>(
4036 input: &[u8],
4037 output: &mut [u8],
4038 wrap: LineWrap,
4039) -> Result<usize, DecodeError> {
4040 let mut bytes = WrappedBytes::new(input, wrap)?;
4041 let mut chunk = [0u8; 4];
4042 let mut indexes = [0usize; 4];
4043 let mut chunk_len = 0;
4044 let mut write = 0;
4045 let mut terminal_seen = false;
4046
4047 while let Some((index, byte)) = bytes.next_byte()? {
4048 if terminal_seen {
4049 return Err(DecodeError::InvalidPadding { index });
4050 }
4051
4052 chunk[chunk_len] = byte;
4053 indexes[chunk_len] = index;
4054 chunk_len += 1;
4055
4056 if chunk_len == 4 {
4057 let available = output.len();
4058 let output_tail = output.get_mut(write..).ok_or(DecodeError::OutputTooSmall {
4059 required: write,
4060 available,
4061 })?;
4062 let written = decode_chunk::<A, PAD>(chunk, output_tail)
4063 .map_err(|err| map_chunk_error(err, &indexes))?;
4064 write += written;
4065 terminal_seen = written < 3;
4066 chunk_len = 0;
4067 }
4068 }
4069
4070 if chunk_len == 0 {
4071 return Ok(write);
4072 }
4073 if PAD {
4074 return Err(DecodeError::InvalidLength);
4075 }
4076
4077 decode_tail_unpadded::<A>(&chunk[..chunk_len], &mut output[write..])
4078 .map_err(|err| map_partial_chunk_error(err, &indexes, chunk_len))
4079 .map(|n| write + n)
4080}
4081
4082#[inline]
4083const fn is_legacy_whitespace(byte: u8) -> bool {
4084 matches!(byte, b' ' | b'\t' | b'\r' | b'\n')
4085}
4086
4087fn map_chunk_error(err: DecodeError, indexes: &[usize; 4]) -> DecodeError {
4088 match err {
4089 DecodeError::InvalidByte { index, byte } => DecodeError::InvalidByte {
4090 index: indexes[index],
4091 byte,
4092 },
4093 DecodeError::InvalidPadding { index } => DecodeError::InvalidPadding {
4094 index: indexes[index],
4095 },
4096 DecodeError::InvalidInput
4097 | DecodeError::InvalidLineWrap { .. }
4098 | DecodeError::InvalidLength
4099 | DecodeError::OutputTooSmall { .. } => err,
4100 }
4101}
4102
4103fn map_partial_chunk_error(err: DecodeError, indexes: &[usize; 4], len: usize) -> DecodeError {
4104 match err {
4105 DecodeError::InvalidByte { index, byte } if index < len => DecodeError::InvalidByte {
4106 index: indexes[index],
4107 byte,
4108 },
4109 DecodeError::InvalidPadding { index } if index < len => DecodeError::InvalidPadding {
4110 index: indexes[index],
4111 },
4112 DecodeError::InvalidByte { .. }
4113 | DecodeError::InvalidPadding { .. }
4114 | DecodeError::InvalidLineWrap { .. }
4115 | DecodeError::InvalidInput
4116 | DecodeError::InvalidLength
4117 | DecodeError::OutputTooSmall { .. } => err,
4118 }
4119}
4120
4121fn decode_padded<A: Alphabet>(input: &[u8], output: &mut [u8]) -> Result<usize, DecodeError> {
4122 if !input.len().is_multiple_of(4) {
4123 return Err(DecodeError::InvalidLength);
4124 }
4125 let required = decoded_len_padded(input)?;
4126 if output.len() < required {
4127 return Err(DecodeError::OutputTooSmall {
4128 required,
4129 available: output.len(),
4130 });
4131 }
4132
4133 let mut read = 0;
4134 let mut write = 0;
4135 while read < input.len() {
4136 let chunk = read_quad(input, read)?;
4137 let available = output.len();
4138 let output_tail = output.get_mut(write..).ok_or(DecodeError::OutputTooSmall {
4139 required: write,
4140 available,
4141 })?;
4142 let written = decode_chunk::<A, true>(chunk, output_tail)
4143 .map_err(|err| err.with_index_offset(read))?;
4144 read += 4;
4145 write += written;
4146 if written < 3 && read != input.len() {
4147 return Err(DecodeError::InvalidPadding { index: read - 4 });
4148 }
4149 }
4150 Ok(write)
4151}
4152
4153fn validate_decode<A: Alphabet, const PAD: bool>(input: &[u8]) -> Result<usize, DecodeError> {
4154 if input.is_empty() {
4155 return Ok(0);
4156 }
4157
4158 if PAD {
4159 validate_padded::<A>(input)
4160 } else {
4161 validate_unpadded::<A>(input)
4162 }
4163}
4164
4165fn validate_padded<A: Alphabet>(input: &[u8]) -> Result<usize, DecodeError> {
4166 if !input.len().is_multiple_of(4) {
4167 return Err(DecodeError::InvalidLength);
4168 }
4169 let required = decoded_len_padded(input)?;
4170
4171 let mut read = 0;
4172 while read < input.len() {
4173 let chunk = read_quad(input, read)?;
4174 let written =
4175 validate_chunk::<A, true>(chunk).map_err(|err| err.with_index_offset(read))?;
4176 read += 4;
4177 if written < 3 && read != input.len() {
4178 return Err(DecodeError::InvalidPadding { index: read - 4 });
4179 }
4180 }
4181
4182 Ok(required)
4183}
4184
4185fn validate_unpadded<A: Alphabet>(input: &[u8]) -> Result<usize, DecodeError> {
4186 let required = decoded_len_unpadded(input)?;
4187
4188 let mut read = 0;
4189 while read + 4 <= input.len() {
4190 let chunk = read_quad(input, read)?;
4191 validate_chunk::<A, false>(chunk).map_err(|err| err.with_index_offset(read))?;
4192 read += 4;
4193 }
4194 validate_tail_unpadded::<A>(&input[read..]).map_err(|err| err.with_index_offset(read))?;
4195
4196 Ok(required)
4197}
4198
4199fn decode_unpadded<A: Alphabet>(input: &[u8], output: &mut [u8]) -> Result<usize, DecodeError> {
4200 let required = decoded_len_unpadded(input)?;
4201 if output.len() < required {
4202 return Err(DecodeError::OutputTooSmall {
4203 required,
4204 available: output.len(),
4205 });
4206 }
4207
4208 let mut read = 0;
4209 let mut write = 0;
4210 while read + 4 <= input.len() {
4211 let chunk = read_quad(input, read)?;
4212 let available = output.len();
4213 let output_tail = output.get_mut(write..).ok_or(DecodeError::OutputTooSmall {
4214 required: write,
4215 available,
4216 })?;
4217 let written = decode_chunk::<A, false>(chunk, output_tail)
4218 .map_err(|err| err.with_index_offset(read))?;
4219 read += 4;
4220 write += written;
4221 }
4222 decode_tail_unpadded::<A>(&input[read..], &mut output[write..])
4223 .map_err(|err| err.with_index_offset(read))
4224 .map(|n| write + n)
4225}
4226
4227fn decoded_len_padded(input: &[u8]) -> Result<usize, DecodeError> {
4228 if input.is_empty() {
4229 return Ok(0);
4230 }
4231 if !input.len().is_multiple_of(4) {
4232 return Err(DecodeError::InvalidLength);
4233 }
4234 let mut padding = 0;
4235 if input[input.len() - 1] == b'=' {
4236 padding += 1;
4237 }
4238 if input[input.len() - 2] == b'=' {
4239 padding += 1;
4240 }
4241 if padding == 0
4242 && let Some(index) = input.iter().position(|byte| *byte == b'=')
4243 {
4244 return Err(DecodeError::InvalidPadding { index });
4245 }
4246 if padding > 0 {
4247 let first_pad = input.len() - padding;
4248 if let Some(index) = input[..first_pad].iter().position(|byte| *byte == b'=') {
4249 return Err(DecodeError::InvalidPadding { index });
4250 }
4251 }
4252 Ok(input.len() / 4 * 3 - padding)
4253}
4254
4255fn decoded_len_unpadded(input: &[u8]) -> Result<usize, DecodeError> {
4256 if input.len() % 4 == 1 {
4257 return Err(DecodeError::InvalidLength);
4258 }
4259 if let Some(index) = input.iter().position(|byte| *byte == b'=') {
4260 return Err(DecodeError::InvalidPadding { index });
4261 }
4262 Ok(decoded_capacity(input.len()))
4263}
4264
4265fn read_quad(input: &[u8], offset: usize) -> Result<[u8; 4], DecodeError> {
4266 let end = offset.checked_add(4).ok_or(DecodeError::InvalidLength)?;
4267 match input.get(offset..end) {
4268 Some([b0, b1, b2, b3]) => Ok([*b0, *b1, *b2, *b3]),
4269 _ => Err(DecodeError::InvalidLength),
4270 }
4271}
4272
4273fn first_padding_index(input: [u8; 4]) -> usize {
4274 let [b0, b1, b2, b3] = input;
4275 if b0 == b'=' {
4276 0
4277 } else if b1 == b'=' {
4278 1
4279 } else if b2 == b'=' {
4280 2
4281 } else if b3 == b'=' {
4282 3
4283 } else {
4284 0
4285 }
4286}
4287
4288fn validate_chunk<A: Alphabet, const PAD: bool>(input: [u8; 4]) -> Result<usize, DecodeError> {
4289 let [b0, b1, b2, b3] = input;
4290 let _v0 = decode_byte::<A>(b0, 0)?;
4291 let v1 = decode_byte::<A>(b1, 1)?;
4292
4293 match (b2, b3) {
4294 (b'=', b'=') if PAD => {
4295 if v1 & 0b0000_1111 != 0 {
4296 return Err(DecodeError::InvalidPadding { index: 1 });
4297 }
4298 Ok(1)
4299 }
4300 (b'=', _) if PAD => Err(DecodeError::InvalidPadding { index: 2 }),
4301 (_, b'=') if PAD => {
4302 let v2 = decode_byte::<A>(b2, 2)?;
4303 if v2 & 0b0000_0011 != 0 {
4304 return Err(DecodeError::InvalidPadding { index: 2 });
4305 }
4306 Ok(2)
4307 }
4308 (b'=', _) | (_, b'=') => Err(DecodeError::InvalidPadding {
4309 index: first_padding_index(input),
4310 }),
4311 _ => {
4312 decode_byte::<A>(b2, 2)?;
4313 decode_byte::<A>(b3, 3)?;
4314 Ok(3)
4315 }
4316 }
4317}
4318
4319fn decode_chunk<A: Alphabet, const PAD: bool>(
4320 input: [u8; 4],
4321 output: &mut [u8],
4322) -> Result<usize, DecodeError> {
4323 let [b0, b1, b2, b3] = input;
4324 let v0 = decode_byte::<A>(b0, 0)?;
4325 let v1 = decode_byte::<A>(b1, 1)?;
4326
4327 match (b2, b3) {
4328 (b'=', b'=') if PAD => {
4329 if output.is_empty() {
4330 return Err(DecodeError::OutputTooSmall {
4331 required: 1,
4332 available: output.len(),
4333 });
4334 }
4335 if v1 & 0b0000_1111 != 0 {
4336 return Err(DecodeError::InvalidPadding { index: 1 });
4337 }
4338 output[0] = (v0 << 2) | (v1 >> 4);
4339 Ok(1)
4340 }
4341 (b'=', _) if PAD => Err(DecodeError::InvalidPadding { index: 2 }),
4342 (_, b'=') if PAD => {
4343 if output.len() < 2 {
4344 return Err(DecodeError::OutputTooSmall {
4345 required: 2,
4346 available: output.len(),
4347 });
4348 }
4349 let v2 = decode_byte::<A>(b2, 2)?;
4350 if v2 & 0b0000_0011 != 0 {
4351 return Err(DecodeError::InvalidPadding { index: 2 });
4352 }
4353 output[0] = (v0 << 2) | (v1 >> 4);
4354 output[1] = (v1 << 4) | (v2 >> 2);
4355 Ok(2)
4356 }
4357 (b'=', _) | (_, b'=') => Err(DecodeError::InvalidPadding {
4358 index: first_padding_index(input),
4359 }),
4360 _ => {
4361 if output.len() < 3 {
4362 return Err(DecodeError::OutputTooSmall {
4363 required: 3,
4364 available: output.len(),
4365 });
4366 }
4367 let v2 = decode_byte::<A>(b2, 2)?;
4368 let v3 = decode_byte::<A>(b3, 3)?;
4369 output[0] = (v0 << 2) | (v1 >> 4);
4370 output[1] = (v1 << 4) | (v2 >> 2);
4371 output[2] = (v2 << 6) | v3;
4372 Ok(3)
4373 }
4374 }
4375}
4376
4377fn validate_tail_unpadded<A: Alphabet>(input: &[u8]) -> Result<(), DecodeError> {
4378 match input.len() {
4379 0 => Ok(()),
4380 2 => {
4381 decode_byte::<A>(input[0], 0)?;
4382 let v1 = decode_byte::<A>(input[1], 1)?;
4383 if v1 & 0b0000_1111 != 0 {
4384 return Err(DecodeError::InvalidPadding { index: 1 });
4385 }
4386 Ok(())
4387 }
4388 3 => {
4389 decode_byte::<A>(input[0], 0)?;
4390 decode_byte::<A>(input[1], 1)?;
4391 let v2 = decode_byte::<A>(input[2], 2)?;
4392 if v2 & 0b0000_0011 != 0 {
4393 return Err(DecodeError::InvalidPadding { index: 2 });
4394 }
4395 Ok(())
4396 }
4397 _ => Err(DecodeError::InvalidLength),
4398 }
4399}
4400
4401fn decode_tail_unpadded<A: Alphabet>(
4402 input: &[u8],
4403 output: &mut [u8],
4404) -> Result<usize, DecodeError> {
4405 match input.len() {
4406 0 => Ok(0),
4407 2 => {
4408 if output.is_empty() {
4409 return Err(DecodeError::OutputTooSmall {
4410 required: 1,
4411 available: output.len(),
4412 });
4413 }
4414 let v0 = decode_byte::<A>(input[0], 0)?;
4415 let v1 = decode_byte::<A>(input[1], 1)?;
4416 if v1 & 0b0000_1111 != 0 {
4417 return Err(DecodeError::InvalidPadding { index: 1 });
4418 }
4419 output[0] = (v0 << 2) | (v1 >> 4);
4420 Ok(1)
4421 }
4422 3 => {
4423 if output.len() < 2 {
4424 return Err(DecodeError::OutputTooSmall {
4425 required: 2,
4426 available: output.len(),
4427 });
4428 }
4429 let v0 = decode_byte::<A>(input[0], 0)?;
4430 let v1 = decode_byte::<A>(input[1], 1)?;
4431 let v2 = decode_byte::<A>(input[2], 2)?;
4432 if v2 & 0b0000_0011 != 0 {
4433 return Err(DecodeError::InvalidPadding { index: 2 });
4434 }
4435 output[0] = (v0 << 2) | (v1 >> 4);
4436 output[1] = (v1 << 4) | (v2 >> 2);
4437 Ok(2)
4438 }
4439 _ => Err(DecodeError::InvalidLength),
4440 }
4441}
4442
4443fn decode_byte<A: Alphabet>(byte: u8, index: usize) -> Result<u8, DecodeError> {
4444 A::decode(byte).ok_or(DecodeError::InvalidByte { index, byte })
4445}
4446
4447fn ct_decode_slice<A: Alphabet, const PAD: bool>(
4448 input: &[u8],
4449 output: &mut [u8],
4450) -> Result<usize, DecodeError> {
4451 if input.is_empty() {
4452 return Ok(0);
4453 }
4454
4455 if PAD {
4456 ct_decode_padded::<A>(input, output)
4457 } else {
4458 ct_decode_unpadded::<A>(input, output)
4459 }
4460}
4461
4462fn ct_decode_in_place<A: Alphabet, const PAD: bool>(
4463 buffer: &mut [u8],
4464) -> Result<usize, DecodeError> {
4465 if buffer.is_empty() {
4466 return Ok(0);
4467 }
4468
4469 if PAD {
4470 ct_decode_padded_in_place::<A>(buffer)
4471 } else {
4472 ct_decode_unpadded_in_place::<A>(buffer)
4473 }
4474}
4475
4476fn ct_validate_decode<A: Alphabet, const PAD: bool>(input: &[u8]) -> Result<(), DecodeError> {
4477 if input.is_empty() {
4478 return Ok(());
4479 }
4480
4481 if PAD {
4482 ct_validate_padded::<A>(input)
4483 } else {
4484 ct_validate_unpadded::<A>(input)
4485 }
4486}
4487
4488fn ct_validate_padded<A: Alphabet>(input: &[u8]) -> Result<(), DecodeError> {
4489 if !input.len().is_multiple_of(4) {
4490 return Err(DecodeError::InvalidLength);
4491 }
4492
4493 let padding = ct_padding_len(input);
4494 let mut invalid_byte = 0u8;
4495 let mut invalid_padding = 0u8;
4496 let mut read = 0;
4497
4498 while read + 4 < input.len() {
4499 let [b0, b1, b2, b3] = read_quad(input, read)?;
4500 let (_, valid0) = ct_decode_ascii_base64::<A>(b0);
4501 let (_, valid1) = ct_decode_ascii_base64::<A>(b1);
4502 let (_, valid2) = ct_decode_ascii_base64::<A>(b2);
4503 let (_, valid3) = ct_decode_ascii_base64::<A>(b3);
4504
4505 invalid_byte |= !valid0;
4506 invalid_byte |= !valid1;
4507 invalid_byte |= !valid2;
4508 invalid_byte |= !valid3;
4509 invalid_padding |= ct_mask_eq_u8(b2, b'=');
4510 invalid_padding |= ct_mask_eq_u8(b3, b'=');
4511 read += 4;
4512 }
4513
4514 let final_chunk = read_quad(input, read)?;
4515 let (_, final_invalid_byte, final_invalid_padding, _) =
4516 ct_padded_final_quantum::<A>(final_chunk, padding);
4517 invalid_byte |= final_invalid_byte;
4518 invalid_padding |= final_invalid_padding;
4519
4520 report_ct_error(invalid_byte, invalid_padding)
4521}
4522
4523fn ct_validate_unpadded<A: Alphabet>(input: &[u8]) -> Result<(), DecodeError> {
4524 if input.len() % 4 == 1 {
4525 return Err(DecodeError::InvalidLength);
4526 }
4527
4528 let mut invalid_byte = 0u8;
4529 let mut invalid_padding = 0u8;
4530 let mut read = 0;
4531
4532 while read + 4 <= input.len() {
4533 let b0 = input[read];
4534 let b1 = input[read + 1];
4535 let b2 = input[read + 2];
4536 let b3 = input[read + 3];
4537 let (_, valid0) = ct_decode_ascii_base64::<A>(b0);
4538 let (_, valid1) = ct_decode_ascii_base64::<A>(b1);
4539 let (_, valid2) = ct_decode_ascii_base64::<A>(b2);
4540 let (_, valid3) = ct_decode_ascii_base64::<A>(b3);
4541
4542 invalid_byte |= !valid0;
4543 invalid_byte |= !valid1;
4544 invalid_byte |= !valid2;
4545 invalid_byte |= !valid3;
4546 invalid_padding |= ct_mask_eq_u8(b0, b'=');
4547 invalid_padding |= ct_mask_eq_u8(b1, b'=');
4548 invalid_padding |= ct_mask_eq_u8(b2, b'=');
4549 invalid_padding |= ct_mask_eq_u8(b3, b'=');
4550
4551 read += 4;
4552 }
4553
4554 match input.len() - read {
4555 0 => {}
4556 2 => {
4557 let b0 = input[read];
4558 let b1 = input[read + 1];
4559 let (_, valid0) = ct_decode_ascii_base64::<A>(b0);
4560 let (v1, valid1) = ct_decode_ascii_base64::<A>(b1);
4561 invalid_byte |= !valid0;
4562 invalid_byte |= !valid1;
4563 invalid_padding |= ct_mask_eq_u8(b0, b'=');
4564 invalid_padding |= ct_mask_eq_u8(b1, b'=');
4565 invalid_padding |= ct_mask_nonzero_u8(v1 & 0b0000_1111);
4566 }
4567 3 => {
4568 let b0 = input[read];
4569 let b1 = input[read + 1];
4570 let b2 = input[read + 2];
4571 let (_, valid0) = ct_decode_ascii_base64::<A>(b0);
4572 let (_, valid1) = ct_decode_ascii_base64::<A>(b1);
4573 let (v2, valid2) = ct_decode_ascii_base64::<A>(b2);
4574 invalid_byte |= !valid0;
4575 invalid_byte |= !valid1;
4576 invalid_byte |= !valid2;
4577 invalid_padding |= ct_mask_eq_u8(b0, b'=');
4578 invalid_padding |= ct_mask_eq_u8(b1, b'=');
4579 invalid_padding |= ct_mask_eq_u8(b2, b'=');
4580 invalid_padding |= ct_mask_nonzero_u8(v2 & 0b0000_0011);
4581 }
4582 _ => return Err(DecodeError::InvalidLength),
4583 }
4584
4585 report_ct_error(invalid_byte, invalid_padding)
4586}
4587
4588fn ct_padded_final_quantum<A: Alphabet>(
4589 input: [u8; 4],
4590 padding: usize,
4591) -> ([u8; 3], u8, u8, usize) {
4592 let [b0, b1, b2, b3] = input;
4593 let (v0, valid0) = ct_decode_ascii_base64::<A>(b0);
4594 let (v1, valid1) = ct_decode_ascii_base64::<A>(b1);
4595 let (v2, valid2) = ct_decode_ascii_base64::<A>(b2);
4596 let (v3, valid3) = ct_decode_ascii_base64::<A>(b3);
4597
4598 let padding_byte = padding.to_le_bytes()[0];
4599 let no_padding = ct_mask_eq_u8(padding_byte, 0);
4600 let one_padding = ct_mask_eq_u8(padding_byte, 1);
4601 let two_padding = ct_mask_eq_u8(padding_byte, 2);
4602 let require_v2 = no_padding | one_padding;
4603 let require_v3 = no_padding;
4604
4605 let invalid_byte = !valid0 | !valid1 | (!valid2 & require_v2) | (!valid3 & require_v3);
4606 let invalid_padding = (ct_mask_nonzero_u8(v1 & 0b0000_1111) & two_padding)
4607 | ((ct_mask_eq_u8(b2, b'=') | ct_mask_nonzero_u8(v2 & 0b0000_0011)) & one_padding)
4608 | ((ct_mask_eq_u8(b2, b'=') | ct_mask_eq_u8(b3, b'=')) & no_padding);
4609
4610 (
4611 [(v0 << 2) | (v1 >> 4), (v1 << 4) | (v2 >> 2), (v2 << 6) | v3],
4612 invalid_byte,
4613 invalid_padding,
4614 3 - padding,
4615 )
4616}
4617
4618fn ct_decode_padded<A: Alphabet>(input: &[u8], output: &mut [u8]) -> Result<usize, DecodeError> {
4619 if !input.len().is_multiple_of(4) {
4620 return Err(DecodeError::InvalidLength);
4621 }
4622
4623 let padding = ct_padding_len(input);
4624 let required = input.len() / 4 * 3 - padding;
4625 if output.len() < required {
4626 return Err(DecodeError::OutputTooSmall {
4627 required,
4628 available: output.len(),
4629 });
4630 }
4631
4632 let mut invalid_byte = 0u8;
4633 let mut invalid_padding = 0u8;
4634 let mut write = 0;
4635 let mut read = 0;
4636
4637 while read + 4 < input.len() {
4638 let [b0, b1, b2, b3] = read_quad(input, read)?;
4639 let (v0, valid0) = ct_decode_ascii_base64::<A>(b0);
4640 let (v1, valid1) = ct_decode_ascii_base64::<A>(b1);
4641 let (v2, valid2) = ct_decode_ascii_base64::<A>(b2);
4642 let (v3, valid3) = ct_decode_ascii_base64::<A>(b3);
4643
4644 invalid_byte |= !valid0;
4645 invalid_byte |= !valid1;
4646 invalid_byte |= !valid2;
4647 invalid_byte |= !valid3;
4648 invalid_padding |= ct_mask_eq_u8(b2, b'=');
4649 invalid_padding |= ct_mask_eq_u8(b3, b'=');
4650 output[write] = (v0 << 2) | (v1 >> 4);
4651 output[write + 1] = (v1 << 4) | (v2 >> 2);
4652 output[write + 2] = (v2 << 6) | v3;
4653 write += 3;
4654 read += 4;
4655 }
4656
4657 let final_chunk = read_quad(input, read)?;
4658 let (final_bytes, final_invalid_byte, final_invalid_padding, final_written) =
4659 ct_padded_final_quantum::<A>(final_chunk, padding);
4660 invalid_byte |= final_invalid_byte;
4661 invalid_padding |= final_invalid_padding;
4662 output[write..write + final_written].copy_from_slice(&final_bytes[..final_written]);
4663 write += final_written;
4664
4665 report_ct_error(invalid_byte, invalid_padding)?;
4666 Ok(write)
4667}
4668
4669fn ct_decode_padded_in_place<A: Alphabet>(buffer: &mut [u8]) -> Result<usize, DecodeError> {
4670 if !buffer.len().is_multiple_of(4) {
4671 return Err(DecodeError::InvalidLength);
4672 }
4673
4674 let padding = ct_padding_len(buffer);
4675 let required = buffer.len() / 4 * 3 - padding;
4676 debug_assert!(required <= buffer.len());
4677
4678 let mut invalid_byte = 0u8;
4679 let mut invalid_padding = 0u8;
4680 let mut write = 0;
4681 let mut read = 0;
4682
4683 while read + 4 < buffer.len() {
4684 let [b0, b1, b2, b3] = read_quad(buffer, read)?;
4685 let (v0, valid0) = ct_decode_ascii_base64::<A>(b0);
4686 let (v1, valid1) = ct_decode_ascii_base64::<A>(b1);
4687 let (v2, valid2) = ct_decode_ascii_base64::<A>(b2);
4688 let (v3, valid3) = ct_decode_ascii_base64::<A>(b3);
4689
4690 invalid_byte |= !valid0;
4691 invalid_byte |= !valid1;
4692 invalid_byte |= !valid2;
4693 invalid_byte |= !valid3;
4694 invalid_padding |= ct_mask_eq_u8(b2, b'=');
4695 invalid_padding |= ct_mask_eq_u8(b3, b'=');
4696 buffer[write] = (v0 << 2) | (v1 >> 4);
4697 buffer[write + 1] = (v1 << 4) | (v2 >> 2);
4698 buffer[write + 2] = (v2 << 6) | v3;
4699 write += 3;
4700 read += 4;
4701 }
4702
4703 let final_chunk = read_quad(buffer, read)?;
4704 let (final_bytes, final_invalid_byte, final_invalid_padding, final_written) =
4705 ct_padded_final_quantum::<A>(final_chunk, padding);
4706 invalid_byte |= final_invalid_byte;
4707 invalid_padding |= final_invalid_padding;
4708 buffer[write..write + final_written].copy_from_slice(&final_bytes[..final_written]);
4709 write += final_written;
4710
4711 debug_assert_eq!(write, required);
4712 report_ct_error(invalid_byte, invalid_padding)?;
4713 Ok(write)
4714}
4715
4716fn ct_decode_unpadded<A: Alphabet>(input: &[u8], output: &mut [u8]) -> Result<usize, DecodeError> {
4717 if input.len() % 4 == 1 {
4718 return Err(DecodeError::InvalidLength);
4719 }
4720
4721 let required = decoded_capacity(input.len());
4722 if output.len() < required {
4723 return Err(DecodeError::OutputTooSmall {
4724 required,
4725 available: output.len(),
4726 });
4727 }
4728
4729 let mut invalid_byte = 0u8;
4730 let mut invalid_padding = 0u8;
4731 let mut write = 0;
4732 let mut read = 0;
4733
4734 while read + 4 <= input.len() {
4735 let b0 = input[read];
4736 let b1 = input[read + 1];
4737 let b2 = input[read + 2];
4738 let b3 = input[read + 3];
4739 let (v0, valid0) = ct_decode_ascii_base64::<A>(b0);
4740 let (v1, valid1) = ct_decode_ascii_base64::<A>(b1);
4741 let (v2, valid2) = ct_decode_ascii_base64::<A>(b2);
4742 let (v3, valid3) = ct_decode_ascii_base64::<A>(b3);
4743
4744 invalid_byte |= !valid0;
4745 invalid_byte |= !valid1;
4746 invalid_byte |= !valid2;
4747 invalid_byte |= !valid3;
4748 invalid_padding |= ct_mask_eq_u8(b0, b'=');
4749 invalid_padding |= ct_mask_eq_u8(b1, b'=');
4750 invalid_padding |= ct_mask_eq_u8(b2, b'=');
4751 invalid_padding |= ct_mask_eq_u8(b3, b'=');
4752
4753 output[write] = (v0 << 2) | (v1 >> 4);
4754 output[write + 1] = (v1 << 4) | (v2 >> 2);
4755 output[write + 2] = (v2 << 6) | v3;
4756 read += 4;
4757 write += 3;
4758 }
4759
4760 match input.len() - read {
4761 0 => {}
4762 2 => {
4763 let b0 = input[read];
4764 let b1 = input[read + 1];
4765 let (v0, valid0) = ct_decode_ascii_base64::<A>(b0);
4766 let (v1, valid1) = ct_decode_ascii_base64::<A>(b1);
4767 invalid_byte |= !valid0;
4768 invalid_byte |= !valid1;
4769 invalid_padding |= ct_mask_eq_u8(b0, b'=');
4770 invalid_padding |= ct_mask_eq_u8(b1, b'=');
4771 invalid_padding |= ct_mask_nonzero_u8(v1 & 0b0000_1111);
4772 output[write] = (v0 << 2) | (v1 >> 4);
4773 write += 1;
4774 }
4775 3 => {
4776 let b0 = input[read];
4777 let b1 = input[read + 1];
4778 let b2 = input[read + 2];
4779 let (v0, valid0) = ct_decode_ascii_base64::<A>(b0);
4780 let (v1, valid1) = ct_decode_ascii_base64::<A>(b1);
4781 let (v2, valid2) = ct_decode_ascii_base64::<A>(b2);
4782 invalid_byte |= !valid0;
4783 invalid_byte |= !valid1;
4784 invalid_byte |= !valid2;
4785 invalid_padding |= ct_mask_eq_u8(b0, b'=');
4786 invalid_padding |= ct_mask_eq_u8(b1, b'=');
4787 invalid_padding |= ct_mask_eq_u8(b2, b'=');
4788 invalid_padding |= ct_mask_nonzero_u8(v2 & 0b0000_0011);
4789 output[write] = (v0 << 2) | (v1 >> 4);
4790 output[write + 1] = (v1 << 4) | (v2 >> 2);
4791 write += 2;
4792 }
4793 _ => return Err(DecodeError::InvalidLength),
4794 }
4795
4796 report_ct_error(invalid_byte, invalid_padding)?;
4797 Ok(write)
4798}
4799
4800fn ct_decode_unpadded_in_place<A: Alphabet>(buffer: &mut [u8]) -> Result<usize, DecodeError> {
4801 if buffer.len() % 4 == 1 {
4802 return Err(DecodeError::InvalidLength);
4803 }
4804
4805 let required = decoded_capacity(buffer.len());
4806 debug_assert!(required <= buffer.len());
4807
4808 let mut invalid_byte = 0u8;
4809 let mut invalid_padding = 0u8;
4810 let mut write = 0;
4811 let mut read = 0;
4812
4813 while read + 4 <= buffer.len() {
4814 let b0 = buffer[read];
4815 let b1 = buffer[read + 1];
4816 let b2 = buffer[read + 2];
4817 let b3 = buffer[read + 3];
4818 let (v0, valid0) = ct_decode_ascii_base64::<A>(b0);
4819 let (v1, valid1) = ct_decode_ascii_base64::<A>(b1);
4820 let (v2, valid2) = ct_decode_ascii_base64::<A>(b2);
4821 let (v3, valid3) = ct_decode_ascii_base64::<A>(b3);
4822
4823 invalid_byte |= !valid0;
4824 invalid_byte |= !valid1;
4825 invalid_byte |= !valid2;
4826 invalid_byte |= !valid3;
4827 invalid_padding |= ct_mask_eq_u8(b0, b'=');
4828 invalid_padding |= ct_mask_eq_u8(b1, b'=');
4829 invalid_padding |= ct_mask_eq_u8(b2, b'=');
4830 invalid_padding |= ct_mask_eq_u8(b3, b'=');
4831
4832 buffer[write] = (v0 << 2) | (v1 >> 4);
4833 buffer[write + 1] = (v1 << 4) | (v2 >> 2);
4834 buffer[write + 2] = (v2 << 6) | v3;
4835 read += 4;
4836 write += 3;
4837 }
4838
4839 match buffer.len() - read {
4840 0 => {}
4841 2 => {
4842 let b0 = buffer[read];
4843 let b1 = buffer[read + 1];
4844 let (v0, valid0) = ct_decode_ascii_base64::<A>(b0);
4845 let (v1, valid1) = ct_decode_ascii_base64::<A>(b1);
4846 invalid_byte |= !valid0;
4847 invalid_byte |= !valid1;
4848 invalid_padding |= ct_mask_eq_u8(b0, b'=');
4849 invalid_padding |= ct_mask_eq_u8(b1, b'=');
4850 invalid_padding |= ct_mask_nonzero_u8(v1 & 0b0000_1111);
4851 buffer[write] = (v0 << 2) | (v1 >> 4);
4852 write += 1;
4853 }
4854 3 => {
4855 let b0 = buffer[read];
4856 let b1 = buffer[read + 1];
4857 let b2 = buffer[read + 2];
4858 let (v0, valid0) = ct_decode_ascii_base64::<A>(b0);
4859 let (v1, valid1) = ct_decode_ascii_base64::<A>(b1);
4860 let (v2, valid2) = ct_decode_ascii_base64::<A>(b2);
4861 invalid_byte |= !valid0;
4862 invalid_byte |= !valid1;
4863 invalid_byte |= !valid2;
4864 invalid_padding |= ct_mask_eq_u8(b0, b'=');
4865 invalid_padding |= ct_mask_eq_u8(b1, b'=');
4866 invalid_padding |= ct_mask_eq_u8(b2, b'=');
4867 invalid_padding |= ct_mask_nonzero_u8(v2 & 0b0000_0011);
4868 buffer[write] = (v0 << 2) | (v1 >> 4);
4869 buffer[write + 1] = (v1 << 4) | (v2 >> 2);
4870 write += 2;
4871 }
4872 _ => return Err(DecodeError::InvalidLength),
4873 }
4874
4875 debug_assert_eq!(write, required);
4876 report_ct_error(invalid_byte, invalid_padding)?;
4877 Ok(write)
4878}
4879
4880#[inline]
4881fn ct_decode_ascii_base64<A: Alphabet>(byte: u8) -> (u8, u8) {
4882 let upper = ct_mask_lt_u8(byte.wrapping_sub(b'A'), 26);
4883 let lower = ct_mask_lt_u8(byte.wrapping_sub(b'a'), 26);
4884 let digit = ct_mask_lt_u8(byte.wrapping_sub(b'0'), 10);
4885 let value_62 = ct_mask_eq_u8(byte, A::ENCODE[62]);
4886 let value_63 = ct_mask_eq_u8(byte, A::ENCODE[63]);
4887 let valid = upper | lower | digit | value_62 | value_63;
4888
4889 let decoded = (byte.wrapping_sub(b'A') & upper)
4890 | (byte.wrapping_sub(b'a').wrapping_add(26) & lower)
4891 | (byte.wrapping_sub(b'0').wrapping_add(52) & digit)
4892 | (0x3e & value_62)
4893 | (0x3f & value_63);
4894
4895 (decoded, valid)
4896}
4897
4898fn ct_padding_len(input: &[u8]) -> usize {
4899 let last = input[input.len() - 1];
4900 let before_last = input[input.len() - 2];
4901 usize::from(ct_mask_eq_u8(last, b'=') & 1) + usize::from(ct_mask_eq_u8(before_last, b'=') & 1)
4902}
4903
4904fn report_ct_error(invalid_byte: u8, invalid_padding: u8) -> Result<(), DecodeError> {
4905 if (invalid_byte | invalid_padding) != 0 {
4906 Err(DecodeError::InvalidInput)
4907 } else {
4908 Ok(())
4909 }
4910}
4911
4912#[cfg(kani)]
4913mod kani_proofs {
4914 use super::{STANDARD, checked_encoded_len, ct, decoded_capacity};
4915
4916 #[kani::proof]
4917 fn checked_encoded_len_is_bounded_for_small_inputs() {
4918 let len = usize::from(kani::any::<u8>());
4919 let padded = kani::any::<bool>();
4920 let encoded = checked_encoded_len(len, padded).expect("u8 input length cannot overflow");
4921
4922 assert!(encoded >= len);
4923 assert!(encoded <= len / 3 * 4 + 4);
4924 }
4925
4926 #[kani::proof]
4927 fn decoded_capacity_is_bounded_for_small_inputs() {
4928 let len = usize::from(kani::any::<u8>());
4929 let capacity = decoded_capacity(len);
4930
4931 assert!(capacity <= len / 4 * 3 + 2);
4932 }
4933
4934 #[kani::proof]
4935 #[kani::unwind(3)]
4936 fn standard_in_place_decode_returns_prefix_within_buffer() {
4937 let mut buffer = kani::any::<[u8; 8]>();
4938 let result = STANDARD.decode_in_place(&mut buffer);
4939
4940 if let Ok(decoded) = result {
4941 assert!(decoded.len() <= 8);
4942 }
4943 }
4944
4945 #[kani::proof]
4946 #[kani::unwind(3)]
4947 fn standard_decode_slice_returns_written_within_output() {
4948 let input = kani::any::<[u8; 4]>();
4949 let mut output = kani::any::<[u8; 3]>();
4950 let result = STANDARD.decode_slice(&input, &mut output);
4951
4952 if let Ok(written) = result {
4953 assert!(written <= output.len());
4954 }
4955 }
4956
4957 #[kani::proof]
4958 #[kani::unwind(3)]
4959 fn standard_decode_slice_clear_tail_clears_output_on_error() {
4960 let input = kani::any::<[u8; 4]>();
4961 let mut output = kani::any::<[u8; 3]>();
4962 let result = STANDARD.decode_slice_clear_tail(&input, &mut output);
4963
4964 if result.is_err() {
4965 assert!(output.iter().all(|byte| *byte == 0));
4966 }
4967 }
4968
4969 #[kani::proof]
4970 #[kani::unwind(3)]
4971 fn standard_encode_slice_returns_written_within_output() {
4972 let input = kani::any::<[u8; 3]>();
4973 let mut output = kani::any::<[u8; 4]>();
4974 let result = STANDARD.encode_slice(&input, &mut output);
4975
4976 if let Ok(written) = result {
4977 assert!(written <= output.len());
4978 }
4979 }
4980
4981 #[kani::proof]
4982 #[kani::unwind(4)]
4983 fn standard_encode_in_place_returns_prefix_within_buffer() {
4984 let mut buffer = kani::any::<[u8; 8]>();
4985 let input_len = usize::from(kani::any::<u8>() % 9);
4986 let result = STANDARD.encode_in_place(&mut buffer, input_len);
4987
4988 if let Ok(encoded) = result {
4989 assert!(encoded.len() <= 8);
4990 }
4991 }
4992
4993 #[kani::proof]
4994 #[kani::unwind(3)]
4995 fn standard_clear_tail_decode_clears_buffer_on_error() {
4996 let mut buffer = kani::any::<[u8; 4]>();
4997 let result = STANDARD.decode_in_place_clear_tail(&mut buffer);
4998
4999 if result.is_err() {
5000 assert!(buffer.iter().all(|byte| *byte == 0));
5001 }
5002 }
5003
5004 #[kani::proof]
5005 #[kani::unwind(3)]
5006 fn ct_standard_decode_slice_returns_written_within_output() {
5007 let input = kani::any::<[u8; 4]>();
5008 let mut output = kani::any::<[u8; 3]>();
5009 let result = ct::STANDARD.decode_slice(&input, &mut output);
5010
5011 if let Ok(written) = result {
5012 assert!(written <= output.len());
5013 }
5014 }
5015
5016 #[kani::proof]
5017 #[kani::unwind(3)]
5018 fn ct_standard_decode_slice_clear_tail_clears_output_on_error() {
5019 let input = kani::any::<[u8; 4]>();
5020 let mut output = kani::any::<[u8; 3]>();
5021 let result = ct::STANDARD.decode_slice_clear_tail(&input, &mut output);
5022
5023 if result.is_err() {
5024 assert!(output.iter().all(|byte| *byte == 0));
5025 }
5026 }
5027
5028 #[kani::proof]
5029 #[kani::unwind(3)]
5030 fn ct_standard_decode_in_place_clear_tail_clears_buffer_on_error() {
5031 let mut buffer = kani::any::<[u8; 4]>();
5032 let result = ct::STANDARD.decode_in_place_clear_tail(&mut buffer);
5033
5034 if result.is_err() {
5035 assert!(buffer.iter().all(|byte| *byte == 0));
5036 }
5037 }
5038
5039 #[kani::proof]
5040 #[kani::unwind(3)]
5041 fn ct_standard_validate_matches_decode_for_one_quantum() {
5042 let input = kani::any::<[u8; 4]>();
5043 let mut output = kani::any::<[u8; 3]>();
5044
5045 let validate_ok = ct::STANDARD.validate_result(&input).is_ok();
5046 let decode_ok = ct::STANDARD.decode_slice(&input, &mut output).is_ok();
5047
5048 assert!(validate_ok == decode_ok);
5049 }
5050}
5051
5052#[cfg(test)]
5053mod tests {
5054 use super::*;
5055
5056 fn fill_pattern(output: &mut [u8], seed: usize) {
5057 for (index, byte) in output.iter_mut().enumerate() {
5058 let value = (index * 73 + seed * 19) % 256;
5059 *byte = u8::try_from(value).unwrap();
5060 }
5061 }
5062
5063 fn assert_encode_backend_matches_scalar<A, const PAD: bool>(input: &[u8])
5064 where
5065 A: Alphabet,
5066 {
5067 let engine = Engine::<A, PAD>::new();
5068 let mut dispatched = [0x55; 256];
5069 let mut scalar = [0xaa; 256];
5070
5071 let dispatched_result = engine.encode_slice(input, &mut dispatched);
5072 let scalar_result = backend::scalar_reference_encode_slice::<A, PAD>(input, &mut scalar);
5073
5074 assert_eq!(dispatched_result, scalar_result);
5075 if let Ok(written) = dispatched_result {
5076 assert_eq!(&dispatched[..written], &scalar[..written]);
5077 }
5078
5079 let required = checked_encoded_len(input.len(), PAD).unwrap();
5080 if required > 0 {
5081 let mut dispatched_short = [0x55; 256];
5082 let mut scalar_short = [0xaa; 256];
5083 let available = required - 1;
5084
5085 assert_eq!(
5086 engine.encode_slice(input, &mut dispatched_short[..available]),
5087 backend::scalar_reference_encode_slice::<A, PAD>(
5088 input,
5089 &mut scalar_short[..available],
5090 )
5091 );
5092 }
5093 }
5094
5095 fn assert_decode_backend_matches_scalar<A, const PAD: bool>(input: &[u8])
5096 where
5097 A: Alphabet,
5098 {
5099 let engine = Engine::<A, PAD>::new();
5100 let mut dispatched = [0x55; 128];
5101 let mut scalar = [0xaa; 128];
5102
5103 let dispatched_result = engine.decode_slice(input, &mut dispatched);
5104 let scalar_result = backend::scalar_reference_decode_slice::<A, PAD>(input, &mut scalar);
5105
5106 assert_eq!(dispatched_result, scalar_result);
5107 if let Ok(written) = dispatched_result {
5108 assert_eq!(&dispatched[..written], &scalar[..written]);
5109
5110 if written > 0 {
5111 let mut dispatched_short = [0x55; 128];
5112 let mut scalar_short = [0xaa; 128];
5113 let available = written - 1;
5114
5115 assert_eq!(
5116 engine.decode_slice(input, &mut dispatched_short[..available]),
5117 backend::scalar_reference_decode_slice::<A, PAD>(
5118 input,
5119 &mut scalar_short[..available],
5120 )
5121 );
5122 }
5123 }
5124 }
5125
5126 fn assert_backend_round_trip_matches_scalar<A, const PAD: bool>(input: &[u8])
5127 where
5128 A: Alphabet,
5129 {
5130 assert_encode_backend_matches_scalar::<A, PAD>(input);
5131
5132 let mut encoded = [0; 256];
5133 let encoded_len =
5134 backend::scalar_reference_encode_slice::<A, PAD>(input, &mut encoded).unwrap();
5135 assert_decode_backend_matches_scalar::<A, PAD>(&encoded[..encoded_len]);
5136 }
5137
5138 #[test]
5139 fn backend_dispatch_matches_scalar_reference_for_canonical_inputs() {
5140 let mut input = [0; 128];
5141
5142 for input_len in 0..=input.len() {
5143 fill_pattern(&mut input[..input_len], input_len);
5144 let input = &input[..input_len];
5145
5146 assert_backend_round_trip_matches_scalar::<Standard, true>(input);
5147 assert_backend_round_trip_matches_scalar::<Standard, false>(input);
5148 assert_backend_round_trip_matches_scalar::<UrlSafe, true>(input);
5149 assert_backend_round_trip_matches_scalar::<UrlSafe, false>(input);
5150 }
5151 }
5152
5153 #[test]
5154 fn backend_dispatch_matches_scalar_reference_for_malformed_inputs() {
5155 for input in [
5156 &b"Z"[..],
5157 b"====",
5158 b"AA=A",
5159 b"Zh==",
5160 b"Zm9=",
5161 b"Zm9v$g==",
5162 b"Zm9vZh==",
5163 ] {
5164 assert_decode_backend_matches_scalar::<Standard, true>(input);
5165 }
5166
5167 for input in [&b"Z"[..], b"AA=A", b"Zh", b"Zm9", b"Zm9vYg$"] {
5168 assert_decode_backend_matches_scalar::<Standard, false>(input);
5169 }
5170
5171 assert_decode_backend_matches_scalar::<UrlSafe, true>(b"AA+A");
5172 assert_decode_backend_matches_scalar::<UrlSafe, false>(b"AA/A");
5173 assert_decode_backend_matches_scalar::<Standard, true>(b"AA-A");
5174 assert_decode_backend_matches_scalar::<Standard, false>(b"AA_A");
5175 }
5176
5177 #[cfg(feature = "simd")]
5178 #[test]
5179 fn simd_dispatch_scaffold_keeps_scalar_active() {
5180 assert_eq!(simd::active_backend(), simd::ActiveBackend::Scalar);
5181 let _candidate = simd::detected_candidate();
5182 }
5183
5184 #[test]
5185 fn encodes_standard_vectors() {
5186 let vectors = [
5187 (&b""[..], &b""[..]),
5188 (&b"f"[..], &b"Zg=="[..]),
5189 (&b"fo"[..], &b"Zm8="[..]),
5190 (&b"foo"[..], &b"Zm9v"[..]),
5191 (&b"foob"[..], &b"Zm9vYg=="[..]),
5192 (&b"fooba"[..], &b"Zm9vYmE="[..]),
5193 (&b"foobar"[..], &b"Zm9vYmFy"[..]),
5194 ];
5195 for (input, expected) in vectors {
5196 let mut output = [0u8; 16];
5197 let written = STANDARD.encode_slice(input, &mut output).unwrap();
5198 assert_eq!(&output[..written], expected);
5199 }
5200 }
5201
5202 #[test]
5203 fn decodes_standard_vectors() {
5204 let vectors = [
5205 (&b""[..], &b""[..]),
5206 (&b"Zg=="[..], &b"f"[..]),
5207 (&b"Zm8="[..], &b"fo"[..]),
5208 (&b"Zm9v"[..], &b"foo"[..]),
5209 (&b"Zm9vYg=="[..], &b"foob"[..]),
5210 (&b"Zm9vYmE="[..], &b"fooba"[..]),
5211 (&b"Zm9vYmFy"[..], &b"foobar"[..]),
5212 ];
5213 for (input, expected) in vectors {
5214 let mut output = [0u8; 16];
5215 let written = STANDARD.decode_slice(input, &mut output).unwrap();
5216 assert_eq!(&output[..written], expected);
5217 }
5218 }
5219
5220 #[test]
5221 fn supports_unpadded_url_safe() {
5222 let mut encoded = [0u8; 16];
5223 let written = URL_SAFE_NO_PAD
5224 .encode_slice(b"\xfb\xff", &mut encoded)
5225 .unwrap();
5226 assert_eq!(&encoded[..written], b"-_8");
5227
5228 let mut decoded = [0u8; 2];
5229 let written = URL_SAFE_NO_PAD
5230 .decode_slice(&encoded[..written], &mut decoded)
5231 .unwrap();
5232 assert_eq!(&decoded[..written], b"\xfb\xff");
5233 }
5234
5235 #[test]
5236 fn decodes_in_place() {
5237 let mut buffer = *b"Zm9vYmFy";
5238 let decoded = STANDARD_NO_PAD.decode_in_place(&mut buffer).unwrap();
5239 assert_eq!(decoded, b"foobar");
5240 }
5241
5242 #[test]
5243 fn rejects_non_canonical_padding_bits() {
5244 let mut output = [0u8; 4];
5245 assert_eq!(
5246 STANDARD.decode_slice(b"Zh==", &mut output),
5247 Err(DecodeError::InvalidPadding { index: 1 })
5248 );
5249 assert_eq!(
5250 STANDARD.decode_slice(b"Zm9=", &mut output),
5251 Err(DecodeError::InvalidPadding { index: 2 })
5252 );
5253 }
5254}