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")]
46extern crate alloc;
47
48#[cfg(feature = "simd")]
49mod simd;
50
51pub mod runtime {
57 #[derive(Clone, Copy, Debug, Eq, PartialEq)]
59 #[non_exhaustive]
60 pub enum Backend {
61 Scalar,
63 Avx512Vbmi,
65 Avx2,
67 Neon,
69 }
70
71 impl Backend {
72 #[must_use]
78 pub const fn as_str(self) -> &'static str {
79 match self {
80 Self::Scalar => "scalar",
81 Self::Avx512Vbmi => "avx512-vbmi",
82 Self::Avx2 => "avx2",
83 Self::Neon => "neon",
84 }
85 }
86
87 #[must_use]
100 pub const fn required_cpu_features(self) -> &'static [&'static str] {
101 match self {
102 Self::Scalar => &[],
103 Self::Avx512Vbmi => &["avx512f", "avx512bw", "avx512vl", "avx512vbmi"],
104 Self::Avx2 => &["avx2"],
105 Self::Neon => &["neon"],
106 }
107 }
108 }
109
110 impl core::fmt::Display for Backend {
111 fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
112 formatter.write_str(self.as_str())
113 }
114 }
115
116 #[derive(Clone, Copy, Debug, Eq, PartialEq)]
118 #[non_exhaustive]
119 pub enum SecurityPosture {
120 ScalarOnly,
122 SimdCandidateScalarActive,
124 Accelerated,
126 }
127
128 impl SecurityPosture {
129 #[must_use]
138 pub const fn as_str(self) -> &'static str {
139 match self {
140 Self::ScalarOnly => "scalar-only",
141 Self::SimdCandidateScalarActive => "simd-candidate-scalar-active",
142 Self::Accelerated => "accelerated",
143 }
144 }
145 }
146
147 impl core::fmt::Display for SecurityPosture {
148 fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
149 formatter.write_str(self.as_str())
150 }
151 }
152
153 #[derive(Clone, Copy, Debug, Eq, PartialEq)]
155 #[non_exhaustive]
156 pub enum BackendPolicy {
157 ScalarExecutionOnly,
159 SimdFeatureDisabled,
161 NoDetectedSimdCandidate,
163 HighAssuranceScalarOnly,
166 }
167
168 impl BackendPolicy {
169 #[must_use]
178 pub const fn as_str(self) -> &'static str {
179 match self {
180 Self::ScalarExecutionOnly => "scalar-execution-only",
181 Self::SimdFeatureDisabled => "simd-feature-disabled",
182 Self::NoDetectedSimdCandidate => "no-detected-simd-candidate",
183 Self::HighAssuranceScalarOnly => "high-assurance-scalar-only",
184 }
185 }
186 }
187
188 impl core::fmt::Display for BackendPolicy {
189 fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
190 formatter.write_str(self.as_str())
191 }
192 }
193
194 #[derive(Clone, Copy, Debug, Eq, PartialEq)]
196 pub struct BackendPolicyError {
197 pub policy: BackendPolicy,
199 pub report: BackendReport,
201 }
202
203 impl core::fmt::Display for BackendPolicyError {
204 fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
205 write!(
206 formatter,
207 "runtime backend policy `{}` was not satisfied ({})",
208 self.policy, self.report,
209 )
210 }
211 }
212
213 #[cfg(feature = "std")]
214 impl std::error::Error for BackendPolicyError {}
215
216 #[derive(Clone, Copy, Debug, Eq, PartialEq)]
218 pub struct BackendReport {
219 pub active: Backend,
221 pub candidate: Backend,
223 pub simd_feature_enabled: bool,
225 pub accelerated_backend_active: bool,
227 pub unsafe_boundary_enforced: bool,
229 pub security_posture: SecurityPosture,
231 }
232
233 #[derive(Clone, Copy, Debug, Eq, PartialEq)]
235 pub struct BackendSnapshot {
236 pub active: &'static str,
238 pub candidate: &'static str,
240 pub candidate_required_cpu_features: &'static [&'static str],
242 pub simd_feature_enabled: bool,
244 pub accelerated_backend_active: bool,
246 pub unsafe_boundary_enforced: bool,
248 pub security_posture: &'static str,
250 }
251
252 impl core::fmt::Display for BackendReport {
253 fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
254 write!(
255 formatter,
256 "active={} candidate={} candidate_required_cpu_features=",
257 self.active, self.candidate,
258 )?;
259 write_feature_list(formatter, self.candidate_required_cpu_features())?;
260 write!(
261 formatter,
262 " simd_feature_enabled={} accelerated_backend_active={} unsafe_boundary_enforced={} security_posture={}",
263 self.simd_feature_enabled,
264 self.accelerated_backend_active,
265 self.unsafe_boundary_enforced,
266 self.security_posture,
267 )
268 }
269 }
270
271 impl BackendReport {
272 #[must_use]
282 pub const fn satisfies(self, policy: BackendPolicy) -> bool {
283 match policy {
284 BackendPolicy::ScalarExecutionOnly => {
285 matches!(self.active, Backend::Scalar) && !self.accelerated_backend_active
286 }
287 BackendPolicy::SimdFeatureDisabled => !self.simd_feature_enabled,
288 BackendPolicy::NoDetectedSimdCandidate => matches!(self.candidate, Backend::Scalar),
289 BackendPolicy::HighAssuranceScalarOnly => {
290 matches!(self.active, Backend::Scalar)
291 && matches!(self.candidate, Backend::Scalar)
292 && !self.simd_feature_enabled
293 && !self.accelerated_backend_active
294 && self.unsafe_boundary_enforced
295 }
296 }
297 }
298
299 #[must_use]
310 pub const fn candidate_required_cpu_features(self) -> &'static [&'static str] {
311 self.candidate.required_cpu_features()
312 }
313
314 #[must_use]
323 pub const fn snapshot(self) -> BackendSnapshot {
324 BackendSnapshot {
325 active: self.active.as_str(),
326 candidate: self.candidate.as_str(),
327 candidate_required_cpu_features: self.candidate_required_cpu_features(),
328 simd_feature_enabled: self.simd_feature_enabled,
329 accelerated_backend_active: self.accelerated_backend_active,
330 unsafe_boundary_enforced: self.unsafe_boundary_enforced,
331 security_posture: self.security_posture.as_str(),
332 }
333 }
334 }
335
336 #[must_use]
345 pub fn backend_report() -> BackendReport {
346 let active = active_backend();
347 let candidate = detected_candidate();
348 let accelerated_backend_active = active != Backend::Scalar;
349 let security_posture = if accelerated_backend_active {
350 SecurityPosture::Accelerated
351 } else if candidate != Backend::Scalar {
352 SecurityPosture::SimdCandidateScalarActive
353 } else {
354 SecurityPosture::ScalarOnly
355 };
356
357 BackendReport {
358 active,
359 candidate,
360 simd_feature_enabled: cfg!(feature = "simd"),
361 accelerated_backend_active,
362 unsafe_boundary_enforced: true,
363 security_posture,
364 }
365 }
366
367 pub fn require_backend_policy(policy: BackendPolicy) -> Result<(), BackendPolicyError> {
376 let report = backend_report();
377 if report.satisfies(policy) {
378 Ok(())
379 } else {
380 Err(BackendPolicyError { policy, report })
381 }
382 }
383
384 fn write_feature_list(
385 formatter: &mut core::fmt::Formatter<'_>,
386 features: &[&str],
387 ) -> core::fmt::Result {
388 formatter.write_str("[")?;
389 let mut index = 0;
390 while index < features.len() {
391 if index != 0 {
392 formatter.write_str(",")?;
393 }
394 formatter.write_str(features[index])?;
395 index += 1;
396 }
397 formatter.write_str("]")
398 }
399
400 #[cfg(feature = "simd")]
401 fn active_backend() -> Backend {
402 match super::simd::active_backend() {
403 super::simd::ActiveBackend::Scalar => Backend::Scalar,
404 }
405 }
406
407 #[cfg(not(feature = "simd"))]
408 const fn active_backend() -> Backend {
409 Backend::Scalar
410 }
411
412 #[cfg(feature = "simd")]
413 fn detected_candidate() -> Backend {
414 match super::simd::detected_candidate() {
415 super::simd::Candidate::Scalar => Backend::Scalar,
416 #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
417 super::simd::Candidate::Avx512Vbmi => Backend::Avx512Vbmi,
418 #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
419 super::simd::Candidate::Avx2 => Backend::Avx2,
420 #[cfg(any(target_arch = "aarch64", target_arch = "arm"))]
421 super::simd::Candidate::Neon => Backend::Neon,
422 }
423 }
424
425 #[cfg(not(feature = "simd"))]
426 const fn detected_candidate() -> Backend {
427 Backend::Scalar
428 }
429}
430
431#[cfg(feature = "stream")]
432pub mod stream {
433 use super::{Alphabet, DecodeError, EncodeError, Engine};
463 use std::io::{self, Read, Write};
464
465 struct OutputQueue<const CAP: usize> {
466 buffer: [u8; CAP],
467 start: usize,
468 len: usize,
469 }
470
471 impl<const CAP: usize> OutputQueue<CAP> {
472 const fn new() -> Self {
473 Self {
474 buffer: [0; CAP],
475 start: 0,
476 len: 0,
477 }
478 }
479
480 const fn is_empty(&self) -> bool {
481 self.len == 0
482 }
483
484 fn push_slice(&mut self, input: &[u8]) -> io::Result<()> {
485 if input.len() > self.available_capacity() {
486 return Err(io::Error::new(
487 io::ErrorKind::InvalidInput,
488 "base64 stream output queue capacity exceeded",
489 ));
490 }
491
492 let mut read = 0;
493 while read < input.len() {
494 let write = (self.start + self.len) % CAP;
495 self.buffer[write] = input[read];
496 self.len += 1;
497 read += 1;
498 }
499
500 Ok(())
501 }
502
503 fn pop_front(&mut self) -> Option<u8> {
504 if self.len == 0 {
505 return None;
506 }
507
508 let byte = self.buffer[self.start];
509 self.buffer[self.start] = 0;
510 self.start = (self.start + 1) % CAP;
511 self.len -= 1;
512 if self.len == 0 {
513 self.start = 0;
514 }
515 Some(byte)
516 }
517
518 fn clear_all(&mut self) {
519 crate::wipe_bytes(&mut self.buffer);
520 self.start = 0;
521 self.len = 0;
522 }
523
524 const fn available_capacity(&self) -> usize {
525 CAP - self.len
526 }
527 }
528
529 pub struct Encoder<W, A, const PAD: bool>
531 where
532 A: Alphabet,
533 {
534 inner: Option<W>,
535 engine: Engine<A, PAD>,
536 pending: [u8; 2],
537 pending_len: usize,
538 }
539
540 impl<W, A, const PAD: bool> Encoder<W, A, PAD>
541 where
542 A: Alphabet,
543 {
544 #[must_use]
546 pub const fn new(inner: W, engine: Engine<A, PAD>) -> Self {
547 Self {
548 inner: Some(inner),
549 engine,
550 pending: [0; 2],
551 pending_len: 0,
552 }
553 }
554
555 #[must_use]
557 pub fn get_ref(&self) -> &W {
558 self.inner_ref()
559 }
560
561 pub fn get_mut(&mut self) -> &mut W {
563 self.inner_mut()
564 }
565
566 #[must_use]
570 pub fn into_inner(mut self) -> W {
571 self.take_inner()
572 }
573
574 fn inner_ref(&self) -> &W {
575 match &self.inner {
576 Some(inner) => inner,
577 None => unreachable!("stream encoder inner writer was already taken"),
578 }
579 }
580
581 fn inner_mut(&mut self) -> &mut W {
582 match &mut self.inner {
583 Some(inner) => inner,
584 None => unreachable!("stream encoder inner writer was already taken"),
585 }
586 }
587
588 fn take_inner(&mut self) -> W {
589 match self.inner.take() {
590 Some(inner) => inner,
591 None => unreachable!("stream encoder inner writer was already taken"),
592 }
593 }
594
595 fn clear_pending(&mut self) {
596 crate::wipe_bytes(&mut self.pending);
597 self.pending_len = 0;
598 }
599 }
600
601 impl<W, A, const PAD: bool> Drop for Encoder<W, A, PAD>
602 where
603 A: Alphabet,
604 {
605 fn drop(&mut self) {
606 self.clear_pending();
607 }
608 }
609
610 impl<W, A, const PAD: bool> Encoder<W, A, PAD>
611 where
612 W: Write,
613 A: Alphabet,
614 {
615 pub fn finish(mut self) -> io::Result<W> {
617 self.write_pending_final()?;
618 self.inner_mut().flush()?;
619 Ok(self.take_inner())
620 }
621
622 fn write_pending_final(&mut self) -> io::Result<()> {
623 if self.pending_len == 0 {
624 return Ok(());
625 }
626
627 let mut encoded = [0u8; 4];
628 let written = self
629 .engine
630 .encode_slice(&self.pending[..self.pending_len], &mut encoded)
631 .map_err(encode_error_to_io)?;
632 self.inner_mut().write_all(&encoded[..written])?;
633 self.clear_pending();
634 Ok(())
635 }
636 }
637
638 impl<W, A, const PAD: bool> Write for Encoder<W, A, PAD>
639 where
640 W: Write,
641 A: Alphabet,
642 {
643 fn write(&mut self, input: &[u8]) -> io::Result<usize> {
644 if input.is_empty() {
645 return Ok(0);
646 }
647
648 let mut consumed = 0;
649 if self.pending_len > 0 {
650 let needed = 3 - self.pending_len;
651 if input.len() < needed {
652 self.pending[self.pending_len..self.pending_len + input.len()]
653 .copy_from_slice(input);
654 self.pending_len += input.len();
655 return Ok(input.len());
656 }
657
658 let mut chunk = [0u8; 3];
659 chunk[..self.pending_len].copy_from_slice(&self.pending[..self.pending_len]);
660 chunk[self.pending_len..].copy_from_slice(&input[..needed]);
661
662 let mut encoded = [0u8; 4];
663 let written = self
664 .engine
665 .encode_slice(&chunk, &mut encoded)
666 .map_err(encode_error_to_io)?;
667 self.inner_mut().write_all(&encoded[..written])?;
668 self.clear_pending();
669 consumed += needed;
670 }
671
672 let remaining = &input[consumed..];
673 let full_len = remaining.len() / 3 * 3;
674 let mut offset = 0;
675 let mut encoded = [0u8; 1024];
676 while offset < full_len {
677 let mut take = core::cmp::min(full_len - offset, 768);
678 take -= take % 3;
679 debug_assert!(take > 0);
680
681 let written = self
682 .engine
683 .encode_slice(&remaining[offset..offset + take], &mut encoded)
684 .map_err(encode_error_to_io)?;
685 self.inner_mut().write_all(&encoded[..written])?;
686 offset += take;
687 }
688
689 let tail = &remaining[full_len..];
690 self.pending[..tail.len()].copy_from_slice(tail);
691 self.pending_len = tail.len();
692
693 Ok(input.len())
694 }
695
696 fn flush(&mut self) -> io::Result<()> {
697 self.inner_mut().flush()
698 }
699 }
700
701 fn encode_error_to_io(err: EncodeError) -> io::Error {
702 io::Error::new(io::ErrorKind::InvalidInput, err)
703 }
704
705 pub struct Decoder<W, A, const PAD: bool>
707 where
708 A: Alphabet,
709 {
710 inner: Option<W>,
711 engine: Engine<A, PAD>,
712 pending: [u8; 4],
713 pending_len: usize,
714 finished: bool,
715 }
716
717 impl<W, A, const PAD: bool> Decoder<W, A, PAD>
718 where
719 A: Alphabet,
720 {
721 #[must_use]
723 pub const fn new(inner: W, engine: Engine<A, PAD>) -> Self {
724 Self {
725 inner: Some(inner),
726 engine,
727 pending: [0; 4],
728 pending_len: 0,
729 finished: false,
730 }
731 }
732
733 #[must_use]
735 pub fn get_ref(&self) -> &W {
736 self.inner_ref()
737 }
738
739 pub fn get_mut(&mut self) -> &mut W {
741 self.inner_mut()
742 }
743
744 #[must_use]
748 pub fn into_inner(mut self) -> W {
749 self.take_inner()
750 }
751
752 fn inner_ref(&self) -> &W {
753 match &self.inner {
754 Some(inner) => inner,
755 None => unreachable!("stream decoder inner writer was already taken"),
756 }
757 }
758
759 fn inner_mut(&mut self) -> &mut W {
760 match &mut self.inner {
761 Some(inner) => inner,
762 None => unreachable!("stream decoder inner writer was already taken"),
763 }
764 }
765
766 fn take_inner(&mut self) -> W {
767 match self.inner.take() {
768 Some(inner) => inner,
769 None => unreachable!("stream decoder inner writer was already taken"),
770 }
771 }
772
773 fn clear_pending(&mut self) {
774 crate::wipe_bytes(&mut self.pending);
775 self.pending_len = 0;
776 }
777 }
778
779 impl<W, A, const PAD: bool> Drop for Decoder<W, A, PAD>
780 where
781 A: Alphabet,
782 {
783 fn drop(&mut self) {
784 self.clear_pending();
785 }
786 }
787
788 impl<W, A, const PAD: bool> Decoder<W, A, PAD>
789 where
790 W: Write,
791 A: Alphabet,
792 {
793 pub fn finish(mut self) -> io::Result<W> {
795 self.write_pending_final()?;
796 self.inner_mut().flush()?;
797 Ok(self.take_inner())
798 }
799
800 fn write_pending_final(&mut self) -> io::Result<()> {
801 if self.pending_len == 0 {
802 return Ok(());
803 }
804
805 let mut decoded = [0u8; 3];
806 let written = self
807 .engine
808 .decode_slice(&self.pending[..self.pending_len], &mut decoded)
809 .map_err(decode_error_to_io)?;
810 self.inner_mut().write_all(&decoded[..written])?;
811 self.clear_pending();
812 Ok(())
813 }
814
815 fn write_full_quad(&mut self, input: [u8; 4]) -> io::Result<()> {
816 let mut decoded = [0u8; 3];
817 let written = self
818 .engine
819 .decode_slice(&input, &mut decoded)
820 .map_err(decode_error_to_io)?;
821 self.inner_mut().write_all(&decoded[..written])?;
822 if written < 3 {
823 self.finished = true;
824 }
825 Ok(())
826 }
827 }
828
829 impl<W, A, const PAD: bool> Write for Decoder<W, A, PAD>
830 where
831 W: Write,
832 A: Alphabet,
833 {
834 fn write(&mut self, input: &[u8]) -> io::Result<usize> {
835 if input.is_empty() {
836 return Ok(0);
837 }
838 if self.finished {
839 return Err(trailing_input_after_padding_error());
840 }
841
842 let mut consumed = 0;
843 if self.pending_len > 0 {
844 let needed = 4 - self.pending_len;
845 if input.len() < needed {
846 self.pending[self.pending_len..self.pending_len + input.len()]
847 .copy_from_slice(input);
848 self.pending_len += input.len();
849 return Ok(input.len());
850 }
851
852 let mut quad = [0u8; 4];
853 quad[..self.pending_len].copy_from_slice(&self.pending[..self.pending_len]);
854 quad[self.pending_len..].copy_from_slice(&input[..needed]);
855 self.write_full_quad(quad)?;
856 self.clear_pending();
857 consumed += needed;
858 if self.finished && consumed < input.len() {
859 return Err(trailing_input_after_padding_error());
860 }
861 }
862
863 let remaining = &input[consumed..];
864 let full_len = remaining.len() / 4 * 4;
865 let mut offset = 0;
866 while offset < full_len {
867 let quad = [
868 remaining[offset],
869 remaining[offset + 1],
870 remaining[offset + 2],
871 remaining[offset + 3],
872 ];
873 self.write_full_quad(quad)?;
874 offset += 4;
875 if self.finished && offset < remaining.len() {
876 return Err(trailing_input_after_padding_error());
877 }
878 }
879
880 let tail = &remaining[full_len..];
881 self.pending[..tail.len()].copy_from_slice(tail);
882 self.pending_len = tail.len();
883
884 Ok(input.len())
885 }
886
887 fn flush(&mut self) -> io::Result<()> {
888 self.inner_mut().flush()
889 }
890 }
891
892 fn decode_error_to_io(err: DecodeError) -> io::Error {
893 io::Error::new(io::ErrorKind::InvalidInput, err)
894 }
895
896 fn trailing_input_after_padding_error() -> io::Error {
897 io::Error::new(
898 io::ErrorKind::InvalidInput,
899 "base64 decoder received trailing input after padding",
900 )
901 }
902
903 pub struct DecoderReader<R, A, const PAD: bool>
910 where
911 A: Alphabet,
912 {
913 inner: Option<R>,
914 engine: Engine<A, PAD>,
915 pending: [u8; 4],
916 pending_len: usize,
917 output: OutputQueue<3>,
918 finished: bool,
919 terminal_seen: bool,
920 }
921
922 impl<R, A, const PAD: bool> DecoderReader<R, A, PAD>
923 where
924 A: Alphabet,
925 {
926 #[must_use]
928 pub fn new(inner: R, engine: Engine<A, PAD>) -> Self {
929 Self {
930 inner: Some(inner),
931 engine,
932 pending: [0; 4],
933 pending_len: 0,
934 output: OutputQueue::new(),
935 finished: false,
936 terminal_seen: false,
937 }
938 }
939
940 #[must_use]
942 pub fn get_ref(&self) -> &R {
943 self.inner_ref()
944 }
945
946 pub fn get_mut(&mut self) -> &mut R {
948 self.inner_mut()
949 }
950
951 #[must_use]
953 pub fn into_inner(mut self) -> R {
954 self.take_inner()
955 }
956
957 fn inner_ref(&self) -> &R {
958 match &self.inner {
959 Some(inner) => inner,
960 None => unreachable!("stream decoder reader inner reader was already taken"),
961 }
962 }
963
964 fn inner_mut(&mut self) -> &mut R {
965 match &mut self.inner {
966 Some(inner) => inner,
967 None => unreachable!("stream decoder reader inner reader was already taken"),
968 }
969 }
970
971 fn take_inner(&mut self) -> R {
972 match self.inner.take() {
973 Some(inner) => inner,
974 None => unreachable!("stream decoder reader inner reader was already taken"),
975 }
976 }
977
978 fn clear_pending(&mut self) {
979 crate::wipe_bytes(&mut self.pending);
980 self.pending_len = 0;
981 }
982 }
983
984 impl<R, A, const PAD: bool> Drop for DecoderReader<R, A, PAD>
985 where
986 A: Alphabet,
987 {
988 fn drop(&mut self) {
989 self.clear_pending();
990 self.output.clear_all();
991 }
992 }
993
994 impl<R, A, const PAD: bool> Read for DecoderReader<R, A, PAD>
995 where
996 R: Read,
997 A: Alphabet,
998 {
999 fn read(&mut self, output: &mut [u8]) -> io::Result<usize> {
1000 if output.is_empty() {
1001 return Ok(0);
1002 }
1003
1004 while self.output.is_empty() && !self.finished {
1005 self.fill_output()?;
1006 }
1007
1008 let mut written = 0;
1009 while written < output.len() {
1010 let Some(byte) = self.output.pop_front() else {
1011 break;
1012 };
1013 output[written] = byte;
1014 written += 1;
1015 }
1016
1017 Ok(written)
1018 }
1019 }
1020
1021 impl<R, A, const PAD: bool> DecoderReader<R, A, PAD>
1022 where
1023 R: Read,
1024 A: Alphabet,
1025 {
1026 fn fill_output(&mut self) -> io::Result<()> {
1027 if self.terminal_seen {
1028 self.finished = true;
1029 return Ok(());
1030 }
1031
1032 let mut input = [0u8; 4];
1033 let available = 4 - self.pending_len;
1034 let read = self.inner_mut().read(&mut input[..available])?;
1035 if read == 0 {
1036 self.finished = true;
1037 self.push_final_pending()?;
1038 return Ok(());
1039 }
1040
1041 self.pending[self.pending_len..self.pending_len + read].copy_from_slice(&input[..read]);
1042 self.pending_len += read;
1043 if self.pending_len < 4 {
1044 return Ok(());
1045 }
1046
1047 let quad = self.pending;
1048 self.clear_pending();
1049 self.push_decoded(&quad)?;
1050 if self.terminal_seen {
1051 self.finished = true;
1052 }
1053 Ok(())
1054 }
1055
1056 fn push_final_pending(&mut self) -> io::Result<()> {
1057 if self.pending_len == 0 {
1058 return Ok(());
1059 }
1060
1061 let mut pending = [0u8; 4];
1062 pending[..self.pending_len].copy_from_slice(&self.pending[..self.pending_len]);
1063 let pending_len = self.pending_len;
1064 self.clear_pending();
1065 self.push_decoded(&pending[..pending_len])
1066 }
1067
1068 fn push_decoded(&mut self, input: &[u8]) -> io::Result<()> {
1069 let mut decoded = [0u8; 3];
1070 let written = self
1071 .engine
1072 .decode_slice(input, &mut decoded)
1073 .map_err(decode_error_to_io)?;
1074 self.output.push_slice(&decoded[..written])?;
1075 if input.len() == 4 && written < 3 {
1076 self.terminal_seen = true;
1077 }
1078 Ok(())
1079 }
1080 }
1081
1082 pub struct EncoderReader<R, A, const PAD: bool>
1084 where
1085 A: Alphabet,
1086 {
1087 inner: Option<R>,
1088 engine: Engine<A, PAD>,
1089 pending: [u8; 2],
1090 pending_len: usize,
1091 output: OutputQueue<1024>,
1092 finished: bool,
1093 }
1094
1095 impl<R, A, const PAD: bool> EncoderReader<R, A, PAD>
1096 where
1097 A: Alphabet,
1098 {
1099 #[must_use]
1101 pub fn new(inner: R, engine: Engine<A, PAD>) -> Self {
1102 Self {
1103 inner: Some(inner),
1104 engine,
1105 pending: [0; 2],
1106 pending_len: 0,
1107 output: OutputQueue::new(),
1108 finished: false,
1109 }
1110 }
1111
1112 #[must_use]
1114 pub fn get_ref(&self) -> &R {
1115 self.inner_ref()
1116 }
1117
1118 pub fn get_mut(&mut self) -> &mut R {
1120 self.inner_mut()
1121 }
1122
1123 #[must_use]
1125 pub fn into_inner(mut self) -> R {
1126 self.take_inner()
1127 }
1128
1129 fn inner_ref(&self) -> &R {
1130 match &self.inner {
1131 Some(inner) => inner,
1132 None => unreachable!("stream encoder reader inner reader was already taken"),
1133 }
1134 }
1135
1136 fn inner_mut(&mut self) -> &mut R {
1137 match &mut self.inner {
1138 Some(inner) => inner,
1139 None => unreachable!("stream encoder reader inner reader was already taken"),
1140 }
1141 }
1142
1143 fn take_inner(&mut self) -> R {
1144 match self.inner.take() {
1145 Some(inner) => inner,
1146 None => unreachable!("stream encoder reader inner reader was already taken"),
1147 }
1148 }
1149
1150 fn clear_pending(&mut self) {
1151 crate::wipe_bytes(&mut self.pending);
1152 self.pending_len = 0;
1153 }
1154 }
1155
1156 impl<R, A, const PAD: bool> Drop for EncoderReader<R, A, PAD>
1157 where
1158 A: Alphabet,
1159 {
1160 fn drop(&mut self) {
1161 self.clear_pending();
1162 self.output.clear_all();
1163 }
1164 }
1165
1166 impl<R, A, const PAD: bool> Read for EncoderReader<R, A, PAD>
1167 where
1168 R: Read,
1169 A: Alphabet,
1170 {
1171 fn read(&mut self, output: &mut [u8]) -> io::Result<usize> {
1172 if output.is_empty() {
1173 return Ok(0);
1174 }
1175
1176 while self.output.is_empty() && !self.finished {
1177 self.fill_output()?;
1178 }
1179
1180 let mut written = 0;
1181 while written < output.len() {
1182 let Some(byte) = self.output.pop_front() else {
1183 break;
1184 };
1185 output[written] = byte;
1186 written += 1;
1187 }
1188
1189 Ok(written)
1190 }
1191 }
1192
1193 impl<R, A, const PAD: bool> EncoderReader<R, A, PAD>
1194 where
1195 R: Read,
1196 A: Alphabet,
1197 {
1198 fn fill_output(&mut self) -> io::Result<()> {
1199 let mut input = [0u8; 768];
1200 let read = self.inner_mut().read(&mut input)?;
1201 if read == 0 {
1202 self.finished = true;
1203 self.push_final_pending()?;
1204 return Ok(());
1205 }
1206
1207 let mut consumed = 0;
1208 if self.pending_len > 0 {
1209 let needed = 3 - self.pending_len;
1210 if read < needed {
1211 self.pending[self.pending_len..self.pending_len + read]
1212 .copy_from_slice(&input[..read]);
1213 self.pending_len += read;
1214 return Ok(());
1215 }
1216
1217 let mut chunk = [0u8; 3];
1218 chunk[..self.pending_len].copy_from_slice(&self.pending[..self.pending_len]);
1219 chunk[self.pending_len..].copy_from_slice(&input[..needed]);
1220 self.push_encoded(&chunk)?;
1221 self.clear_pending();
1222 consumed += needed;
1223 }
1224
1225 let remaining = &input[consumed..read];
1226 let full_len = remaining.len() / 3 * 3;
1227 if full_len > 0 {
1228 self.push_encoded(&remaining[..full_len])?;
1229 }
1230
1231 let tail = &remaining[full_len..];
1232 self.pending[..tail.len()].copy_from_slice(tail);
1233 self.pending_len = tail.len();
1234 Ok(())
1235 }
1236
1237 fn push_final_pending(&mut self) -> io::Result<()> {
1238 if self.pending_len == 0 {
1239 return Ok(());
1240 }
1241
1242 let mut pending = [0u8; 2];
1243 pending[..self.pending_len].copy_from_slice(&self.pending[..self.pending_len]);
1244 let pending_len = self.pending_len;
1245 self.clear_pending();
1246 self.push_encoded(&pending[..pending_len])
1247 }
1248
1249 fn push_encoded(&mut self, input: &[u8]) -> io::Result<()> {
1250 let mut encoded = [0u8; 1024];
1251 let written = self
1252 .engine
1253 .encode_slice(input, &mut encoded)
1254 .map_err(encode_error_to_io)?;
1255 self.output.push_slice(&encoded[..written])?;
1256 Ok(())
1257 }
1258 }
1259}
1260
1261pub mod ct {
1269 use super::{
1270 Alphabet, DecodeError, Standard, UrlSafe, ct_decode_in_place, ct_decode_slice,
1271 ct_validate_decode,
1272 };
1273 use core::marker::PhantomData;
1274
1275 pub const STANDARD: CtEngine<Standard, true> = CtEngine::new();
1277
1278 pub const STANDARD_NO_PAD: CtEngine<Standard, false> = CtEngine::new();
1280
1281 pub const URL_SAFE: CtEngine<UrlSafe, true> = CtEngine::new();
1283
1284 pub const URL_SAFE_NO_PAD: CtEngine<UrlSafe, false> = CtEngine::new();
1286
1287 #[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
1289 pub struct CtEngine<A, const PAD: bool> {
1290 alphabet: PhantomData<A>,
1291 }
1292
1293 impl<A, const PAD: bool> CtEngine<A, PAD>
1294 where
1295 A: Alphabet,
1296 {
1297 #[must_use]
1299 pub const fn new() -> Self {
1300 Self {
1301 alphabet: PhantomData,
1302 }
1303 }
1304
1305 pub fn validate_result(&self, input: &[u8]) -> Result<(), DecodeError> {
1320 ct_validate_decode::<A, PAD>(input)
1321 }
1322
1323 #[must_use]
1337 pub fn validate(&self, input: &[u8]) -> bool {
1338 self.validate_result(input).is_ok()
1339 }
1340
1341 pub fn decode_slice(&self, input: &[u8], output: &mut [u8]) -> Result<usize, DecodeError> {
1364 ct_decode_slice::<A, PAD>(input, output)
1365 }
1366
1367 pub fn decode_slice_clear_tail(
1389 &self,
1390 input: &[u8],
1391 output: &mut [u8],
1392 ) -> Result<usize, DecodeError> {
1393 let written = match self.decode_slice(input, output) {
1394 Ok(written) => written,
1395 Err(err) => {
1396 crate::wipe_bytes(output);
1397 return Err(err);
1398 }
1399 };
1400 crate::wipe_tail(output, written);
1401 Ok(written)
1402 }
1403
1404 pub fn decode_in_place<'a>(
1421 &self,
1422 buffer: &'a mut [u8],
1423 ) -> Result<&'a mut [u8], DecodeError> {
1424 let len = ct_decode_in_place::<A, PAD>(buffer)?;
1425 Ok(&mut buffer[..len])
1426 }
1427
1428 pub fn decode_in_place_clear_tail<'a>(
1445 &self,
1446 buffer: &'a mut [u8],
1447 ) -> Result<&'a mut [u8], DecodeError> {
1448 let len = match ct_decode_in_place::<A, PAD>(buffer) {
1449 Ok(len) => len,
1450 Err(err) => {
1451 crate::wipe_bytes(buffer);
1452 return Err(err);
1453 }
1454 };
1455 crate::wipe_tail(buffer, len);
1456 Ok(&mut buffer[..len])
1457 }
1458 }
1459}
1460
1461pub const STANDARD: Engine<Standard, true> = Engine::new();
1463
1464pub const STANDARD_NO_PAD: Engine<Standard, false> = Engine::new();
1466
1467pub const URL_SAFE: Engine<UrlSafe, true> = Engine::new();
1469
1470pub const URL_SAFE_NO_PAD: Engine<UrlSafe, false> = Engine::new();
1472
1473pub const BCRYPT_NO_PAD: Engine<Bcrypt, false> = Engine::new();
1478
1479pub const CRYPT_NO_PAD: Engine<Crypt, false> = Engine::new();
1484
1485#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1487pub enum LineEnding {
1488 Lf,
1490 CrLf,
1492}
1493
1494impl LineEnding {
1495 #[must_use]
1497 pub const fn as_bytes(self) -> &'static [u8] {
1498 match self {
1499 Self::Lf => b"\n",
1500 Self::CrLf => b"\r\n",
1501 }
1502 }
1503
1504 #[must_use]
1506 pub const fn byte_len(self) -> usize {
1507 match self {
1508 Self::Lf => 1,
1509 Self::CrLf => 2,
1510 }
1511 }
1512}
1513
1514#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1520pub struct LineWrap {
1521 pub line_len: usize,
1523 pub line_ending: LineEnding,
1525}
1526
1527impl LineWrap {
1528 pub const MIME: Self = Self::new(76, LineEnding::CrLf);
1530 pub const PEM: Self = Self::new(64, LineEnding::Lf);
1532 pub const PEM_CRLF: Self = Self::new(64, LineEnding::CrLf);
1534
1535 #[must_use]
1537 pub const fn new(line_len: usize, line_ending: LineEnding) -> Self {
1538 Self {
1539 line_len,
1540 line_ending,
1541 }
1542 }
1543}
1544
1545#[allow(unsafe_code)]
1546fn wipe_bytes(bytes: &mut [u8]) {
1547 for byte in bytes {
1548 unsafe {
1552 core::ptr::write_volatile(byte, 0);
1553 }
1554 }
1555 core::sync::atomic::compiler_fence(core::sync::atomic::Ordering::SeqCst);
1556}
1557
1558fn wipe_tail(bytes: &mut [u8], start: usize) {
1559 wipe_bytes(&mut bytes[start..]);
1560}
1561
1562pub struct EncodedBuffer<const CAP: usize> {
1572 bytes: [u8; CAP],
1573 len: usize,
1574}
1575
1576impl<const CAP: usize> EncodedBuffer<CAP> {
1577 #[must_use]
1579 pub const fn new() -> Self {
1580 Self {
1581 bytes: [0u8; CAP],
1582 len: 0,
1583 }
1584 }
1585
1586 #[must_use]
1588 pub const fn len(&self) -> usize {
1589 self.len
1590 }
1591
1592 #[must_use]
1594 pub const fn is_empty(&self) -> bool {
1595 self.len == 0
1596 }
1597
1598 #[must_use]
1600 pub const fn capacity(&self) -> usize {
1601 CAP
1602 }
1603
1604 #[must_use]
1606 pub fn as_bytes(&self) -> &[u8] {
1607 &self.bytes[..self.len]
1608 }
1609
1610 #[must_use]
1617 pub fn as_str(&self) -> &str {
1618 match core::str::from_utf8(self.as_bytes()) {
1619 Ok(output) => output,
1620 Err(_) => unreachable!("base64 encoder produced non-UTF-8 output"),
1621 }
1622 }
1623
1624 pub fn clear(&mut self) {
1626 wipe_bytes(&mut self.bytes);
1627 self.len = 0;
1628 }
1629
1630 pub fn clear_tail(&mut self) {
1632 wipe_tail(&mut self.bytes, self.len);
1633 }
1634}
1635
1636impl<const CAP: usize> AsRef<[u8]> for EncodedBuffer<CAP> {
1637 fn as_ref(&self) -> &[u8] {
1638 self.as_bytes()
1639 }
1640}
1641
1642impl<const CAP: usize> Clone for EncodedBuffer<CAP> {
1643 fn clone(&self) -> Self {
1644 let mut output = Self::new();
1645 output.bytes[..self.len].copy_from_slice(self.as_bytes());
1646 output.len = self.len;
1647 output
1648 }
1649}
1650
1651impl<const CAP: usize> core::fmt::Debug for EncodedBuffer<CAP> {
1652 fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
1653 formatter
1654 .debug_struct("EncodedBuffer")
1655 .field("bytes", &"<redacted>")
1656 .field("len", &self.len)
1657 .field("capacity", &CAP)
1658 .finish()
1659 }
1660}
1661
1662impl<const CAP: usize> Default for EncodedBuffer<CAP> {
1663 fn default() -> Self {
1664 Self::new()
1665 }
1666}
1667
1668impl<const CAP: usize> Drop for EncodedBuffer<CAP> {
1669 fn drop(&mut self) {
1670 self.clear();
1671 }
1672}
1673
1674impl<const CAP: usize> Eq for EncodedBuffer<CAP> {}
1675
1676impl<const CAP: usize> PartialEq for EncodedBuffer<CAP> {
1677 fn eq(&self, other: &Self) -> bool {
1678 self.as_bytes() == other.as_bytes()
1679 }
1680}
1681
1682#[cfg(feature = "alloc")]
1693pub struct SecretBuffer {
1694 bytes: alloc::vec::Vec<u8>,
1695}
1696
1697#[cfg(feature = "alloc")]
1698impl SecretBuffer {
1699 #[must_use]
1701 pub fn from_vec(bytes: alloc::vec::Vec<u8>) -> Self {
1702 Self { bytes }
1703 }
1704
1705 #[must_use]
1707 pub fn from_slice(bytes: &[u8]) -> Self {
1708 Self {
1709 bytes: bytes.to_vec(),
1710 }
1711 }
1712
1713 #[must_use]
1715 pub fn len(&self) -> usize {
1716 self.bytes.len()
1717 }
1718
1719 #[must_use]
1721 pub fn is_empty(&self) -> bool {
1722 self.bytes.is_empty()
1723 }
1724
1725 #[must_use]
1730 pub fn expose_secret(&self) -> &[u8] {
1731 &self.bytes
1732 }
1733
1734 #[must_use]
1739 pub fn expose_secret_mut(&mut self) -> &mut [u8] {
1740 &mut self.bytes
1741 }
1742
1743 pub fn clear(&mut self) {
1745 wipe_bytes(&mut self.bytes);
1746 self.bytes.clear();
1747 }
1748}
1749
1750#[cfg(feature = "alloc")]
1751impl Clone for SecretBuffer {
1752 fn clone(&self) -> Self {
1753 Self::from_slice(self.expose_secret())
1754 }
1755}
1756
1757#[cfg(feature = "alloc")]
1758impl core::fmt::Debug for SecretBuffer {
1759 fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
1760 formatter
1761 .debug_struct("SecretBuffer")
1762 .field("bytes", &"<redacted>")
1763 .field("len", &self.len())
1764 .finish()
1765 }
1766}
1767
1768#[cfg(feature = "alloc")]
1769impl core::fmt::Display for SecretBuffer {
1770 fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
1771 formatter.write_str("<redacted>")
1772 }
1773}
1774
1775#[cfg(feature = "alloc")]
1776impl Drop for SecretBuffer {
1777 fn drop(&mut self) {
1778 wipe_bytes(&mut self.bytes);
1779 }
1780}
1781
1782#[cfg(feature = "alloc")]
1783impl Eq for SecretBuffer {}
1784
1785#[cfg(feature = "alloc")]
1786impl PartialEq for SecretBuffer {
1787 fn eq(&self, other: &Self) -> bool {
1788 self.expose_secret() == other.expose_secret()
1789 }
1790}
1791
1792#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1798pub struct Profile<A, const PAD: bool> {
1799 engine: Engine<A, PAD>,
1800 wrap: Option<LineWrap>,
1801}
1802
1803impl<A, const PAD: bool> Profile<A, PAD>
1804where
1805 A: Alphabet,
1806{
1807 #[must_use]
1809 pub const fn new(engine: Engine<A, PAD>, wrap: Option<LineWrap>) -> Self {
1810 Self { engine, wrap }
1811 }
1812
1813 #[must_use]
1815 pub const fn engine(&self) -> Engine<A, PAD> {
1816 self.engine
1817 }
1818
1819 #[must_use]
1821 pub const fn line_wrap(&self) -> Option<LineWrap> {
1822 self.wrap
1823 }
1824
1825 pub const fn encoded_len(&self, input_len: usize) -> Result<usize, EncodeError> {
1827 match self.wrap {
1828 Some(wrap) => wrapped_encoded_len(input_len, PAD, wrap),
1829 None => encoded_len(input_len, PAD),
1830 }
1831 }
1832
1833 pub fn decoded_len(&self, input: &[u8]) -> Result<usize, DecodeError> {
1835 match self.wrap {
1836 Some(wrap) => self.engine.decoded_len_wrapped(input, wrap),
1837 None => self.engine.decoded_len(input),
1838 }
1839 }
1840
1841 pub fn validate_result(&self, input: &[u8]) -> Result<(), DecodeError> {
1843 match self.wrap {
1844 Some(wrap) => self.engine.validate_wrapped_result(input, wrap),
1845 None => self.engine.validate_result(input),
1846 }
1847 }
1848
1849 #[must_use]
1851 pub fn validate(&self, input: &[u8]) -> bool {
1852 self.validate_result(input).is_ok()
1853 }
1854
1855 pub fn encode_slice(&self, input: &[u8], output: &mut [u8]) -> Result<usize, EncodeError> {
1857 match self.wrap {
1858 Some(wrap) => self.engine.encode_slice_wrapped(input, output, wrap),
1859 None => self.engine.encode_slice(input, output),
1860 }
1861 }
1862
1863 pub fn encode_slice_clear_tail(
1866 &self,
1867 input: &[u8],
1868 output: &mut [u8],
1869 ) -> Result<usize, EncodeError> {
1870 match self.wrap {
1871 Some(wrap) => self
1872 .engine
1873 .encode_slice_wrapped_clear_tail(input, output, wrap),
1874 None => self.engine.encode_slice_clear_tail(input, output),
1875 }
1876 }
1877
1878 pub fn encode_buffer<const CAP: usize>(
1884 &self,
1885 input: &[u8],
1886 ) -> Result<EncodedBuffer<CAP>, EncodeError> {
1887 let mut output = EncodedBuffer::new();
1888 let written = match self.encode_slice_clear_tail(input, &mut output.bytes) {
1889 Ok(written) => written,
1890 Err(err) => {
1891 output.clear();
1892 return Err(err);
1893 }
1894 };
1895 output.len = written;
1896 Ok(output)
1897 }
1898
1899 pub fn decode_slice(&self, input: &[u8], output: &mut [u8]) -> Result<usize, DecodeError> {
1901 match self.wrap {
1902 Some(wrap) => self.engine.decode_slice_wrapped(input, output, wrap),
1903 None => self.engine.decode_slice(input, output),
1904 }
1905 }
1906
1907 pub fn decode_slice_clear_tail(
1910 &self,
1911 input: &[u8],
1912 output: &mut [u8],
1913 ) -> Result<usize, DecodeError> {
1914 match self.wrap {
1915 Some(wrap) => self
1916 .engine
1917 .decode_slice_wrapped_clear_tail(input, output, wrap),
1918 None => self.engine.decode_slice_clear_tail(input, output),
1919 }
1920 }
1921
1922 #[cfg(feature = "alloc")]
1924 pub fn encode_vec(&self, input: &[u8]) -> Result<alloc::vec::Vec<u8>, EncodeError> {
1925 match self.wrap {
1926 Some(wrap) => self.engine.encode_wrapped_vec(input, wrap),
1927 None => self.engine.encode_vec(input),
1928 }
1929 }
1930
1931 #[cfg(feature = "alloc")]
1933 pub fn encode_secret(&self, input: &[u8]) -> Result<SecretBuffer, EncodeError> {
1934 self.encode_vec(input).map(SecretBuffer::from_vec)
1935 }
1936
1937 #[cfg(feature = "alloc")]
1939 pub fn encode_string(&self, input: &[u8]) -> Result<alloc::string::String, EncodeError> {
1940 match self.wrap {
1941 Some(wrap) => self.engine.encode_wrapped_string(input, wrap),
1942 None => self.engine.encode_string(input),
1943 }
1944 }
1945
1946 #[cfg(feature = "alloc")]
1948 pub fn decode_vec(&self, input: &[u8]) -> Result<alloc::vec::Vec<u8>, DecodeError> {
1949 match self.wrap {
1950 Some(wrap) => self.engine.decode_wrapped_vec(input, wrap),
1951 None => self.engine.decode_vec(input),
1952 }
1953 }
1954
1955 #[cfg(feature = "alloc")]
1957 pub fn decode_secret(&self, input: &[u8]) -> Result<SecretBuffer, DecodeError> {
1958 self.decode_vec(input).map(SecretBuffer::from_vec)
1959 }
1960}
1961
1962pub const MIME: Profile<Standard, true> = Profile::new(STANDARD, Some(LineWrap::MIME));
1964
1965pub const PEM: Profile<Standard, true> = Profile::new(STANDARD, Some(LineWrap::PEM));
1967
1968pub const PEM_CRLF: Profile<Standard, true> = Profile::new(STANDARD, Some(LineWrap::PEM_CRLF));
1970
1971pub const BCRYPT: Profile<Bcrypt, false> = Profile::new(BCRYPT_NO_PAD, None);
1976
1977pub const CRYPT: Profile<Crypt, false> = Profile::new(CRYPT_NO_PAD, None);
1982
1983pub const fn encoded_len(input_len: usize, padded: bool) -> Result<usize, EncodeError> {
1998 match checked_encoded_len(input_len, padded) {
1999 Some(len) => Ok(len),
2000 None => Err(EncodeError::LengthOverflow),
2001 }
2002}
2003
2004pub const fn wrapped_encoded_len(
2018 input_len: usize,
2019 padded: bool,
2020 wrap: LineWrap,
2021) -> Result<usize, EncodeError> {
2022 if wrap.line_len == 0 {
2023 return Err(EncodeError::InvalidLineWrap { line_len: 0 });
2024 }
2025
2026 let Some(encoded) = checked_encoded_len(input_len, padded) else {
2027 return Err(EncodeError::LengthOverflow);
2028 };
2029 if encoded == 0 {
2030 return Ok(0);
2031 }
2032
2033 let breaks = (encoded - 1) / wrap.line_len;
2034 let Some(line_ending_bytes) = breaks.checked_mul(wrap.line_ending.byte_len()) else {
2035 return Err(EncodeError::LengthOverflow);
2036 };
2037 match encoded.checked_add(line_ending_bytes) {
2038 Some(len) => Ok(len),
2039 None => Err(EncodeError::LengthOverflow),
2040 }
2041}
2042
2043#[must_use]
2054pub const fn checked_encoded_len(input_len: usize, padded: bool) -> Option<usize> {
2055 let groups = input_len / 3;
2056 if groups > usize::MAX / 4 {
2057 return None;
2058 }
2059 let full = groups * 4;
2060 let rem = input_len % 3;
2061 if rem == 0 {
2062 Some(full)
2063 } else if padded {
2064 full.checked_add(4)
2065 } else {
2066 full.checked_add(rem + 1)
2067 }
2068}
2069
2070#[must_use]
2081pub const fn decoded_capacity(encoded_len: usize) -> usize {
2082 let rem = encoded_len % 4;
2083 encoded_len / 4 * 3
2084 + if rem == 2 {
2085 1
2086 } else if rem == 3 {
2087 2
2088 } else {
2089 0
2090 }
2091}
2092
2093pub fn decoded_len(input: &[u8], padded: bool) -> Result<usize, DecodeError> {
2107 if padded {
2108 decoded_len_padded(input)
2109 } else {
2110 decoded_len_unpadded(input)
2111 }
2112}
2113
2114pub const fn validate_alphabet(encode: &[u8; 64]) -> Result<(), AlphabetError> {
2127 let mut index = 0;
2128 while index < encode.len() {
2129 let byte = encode[index];
2130 if !is_visible_ascii(byte) {
2131 return Err(AlphabetError::InvalidByte { index, byte });
2132 }
2133 if byte == b'=' {
2134 return Err(AlphabetError::PaddingByte { index });
2135 }
2136
2137 let mut duplicate = index + 1;
2138 while duplicate < encode.len() {
2139 if encode[duplicate] == byte {
2140 return Err(AlphabetError::DuplicateByte {
2141 first: index,
2142 second: duplicate,
2143 byte,
2144 });
2145 }
2146 duplicate += 1;
2147 }
2148
2149 index += 1;
2150 }
2151
2152 Ok(())
2153}
2154
2155#[must_use]
2181pub const fn decode_alphabet_byte(byte: u8, encode: &[u8; 64]) -> Option<u8> {
2182 let mut index = 0;
2183 let mut value = 0;
2184 while index < encode.len() {
2185 if encode[index] == byte {
2186 return Some(value);
2187 }
2188 index += 1;
2189 value += 1;
2190 }
2191 None
2192}
2193
2194pub trait Alphabet {
2196 const ENCODE: [u8; 64];
2198
2199 #[must_use]
2205 fn encode(value: u8) -> u8 {
2206 encode_alphabet_value(value, &Self::ENCODE)
2207 }
2208
2209 fn decode(byte: u8) -> Option<u8>;
2211}
2212
2213const fn is_visible_ascii(byte: u8) -> bool {
2214 byte >= 0x21 && byte <= 0x7e
2215}
2216
2217#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
2219pub struct Standard;
2220
2221impl Alphabet for Standard {
2222 const ENCODE: [u8; 64] = *b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
2223
2224 #[inline]
2225 fn encode(value: u8) -> u8 {
2226 encode_ascii_base64(value, Self::ENCODE[62], Self::ENCODE[63])
2227 }
2228
2229 #[inline]
2230 fn decode(byte: u8) -> Option<u8> {
2231 decode_ascii_base64(byte, Self::ENCODE[62], Self::ENCODE[63])
2232 }
2233}
2234
2235#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
2237pub struct UrlSafe;
2238
2239impl Alphabet for UrlSafe {
2240 const ENCODE: [u8; 64] = *b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
2241
2242 #[inline]
2243 fn encode(value: u8) -> u8 {
2244 encode_ascii_base64(value, Self::ENCODE[62], Self::ENCODE[63])
2245 }
2246
2247 #[inline]
2248 fn decode(byte: u8) -> Option<u8> {
2249 decode_ascii_base64(byte, Self::ENCODE[62], Self::ENCODE[63])
2250 }
2251}
2252
2253#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
2259pub struct Bcrypt;
2260
2261impl Alphabet for Bcrypt {
2262 const ENCODE: [u8; 64] = *b"./ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
2263
2264 #[inline]
2265 fn decode(byte: u8) -> Option<u8> {
2266 decode_alphabet_byte(byte, &Self::ENCODE)
2267 }
2268}
2269
2270#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
2275pub struct Crypt;
2276
2277impl Alphabet for Crypt {
2278 const ENCODE: [u8; 64] = *b"./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
2279
2280 #[inline]
2281 fn decode(byte: u8) -> Option<u8> {
2282 decode_alphabet_byte(byte, &Self::ENCODE)
2283 }
2284}
2285
2286#[inline]
2287const fn encode_base64_value<A: Alphabet>(value: u8) -> u8 {
2288 encode_alphabet_value(value, &A::ENCODE)
2289}
2290
2291#[inline]
2292fn encode_base64_value_runtime<A: Alphabet>(value: u8) -> u8 {
2293 A::encode(value)
2294}
2295
2296#[inline]
2297const fn encode_alphabet_value(value: u8, encode: &[u8; 64]) -> u8 {
2298 let mut output = 0;
2299 let mut index = 0;
2300 let mut candidate = 0;
2301 while index < encode.len() {
2302 output |= encode[index] & ct_mask_eq_u8(value, candidate);
2303 index += 1;
2304 candidate += 1;
2305 }
2306 output
2307}
2308
2309#[inline]
2310const fn encode_ascii_base64(value: u8, value_62_byte: u8, value_63_byte: u8) -> u8 {
2311 let upper = ct_mask_lt_u8(value, 26);
2312 let lower = ct_mask_lt_u8(value.wrapping_sub(26), 26);
2313 let digit = ct_mask_lt_u8(value.wrapping_sub(52), 10);
2314 let value_62 = ct_mask_eq_u8(value, 0x3e);
2315 let value_63 = ct_mask_eq_u8(value, 0x3f);
2316
2317 (value.wrapping_add(b'A') & upper)
2318 | (value.wrapping_sub(26).wrapping_add(b'a') & lower)
2319 | (value.wrapping_sub(52).wrapping_add(b'0') & digit)
2320 | (value_62_byte & value_62)
2321 | (value_63_byte & value_63)
2322}
2323
2324#[inline]
2325fn decode_ascii_base64(byte: u8, value_62_byte: u8, value_63_byte: u8) -> Option<u8> {
2326 let upper = ct_mask_lt_u8(byte.wrapping_sub(b'A'), 26);
2327 let lower = ct_mask_lt_u8(byte.wrapping_sub(b'a'), 26);
2328 let digit = ct_mask_lt_u8(byte.wrapping_sub(b'0'), 10);
2329 let value_62 = ct_mask_eq_u8(byte, value_62_byte);
2330 let value_63 = ct_mask_eq_u8(byte, value_63_byte);
2331 let valid = upper | lower | digit | value_62 | value_63;
2332
2333 let decoded = (byte.wrapping_sub(b'A') & upper)
2334 | (byte.wrapping_sub(b'a').wrapping_add(26) & lower)
2335 | (byte.wrapping_sub(b'0').wrapping_add(52) & digit)
2336 | (0x3e & value_62)
2337 | (0x3f & value_63);
2338
2339 if valid == 0 { None } else { Some(decoded) }
2340}
2341
2342#[inline]
2343const fn ct_mask_bit(bit: u8) -> u8 {
2344 0u8.wrapping_sub(bit & 1)
2345}
2346
2347#[inline]
2348const fn ct_mask_nonzero_u8(value: u8) -> u8 {
2349 let wide = value as u16;
2350 let negative = 0u16.wrapping_sub(wide);
2351 let nonzero = ((wide | negative) >> 8) as u8;
2352 ct_mask_bit(nonzero)
2353}
2354
2355#[inline]
2356const fn ct_mask_eq_u8(left: u8, right: u8) -> u8 {
2357 !ct_mask_nonzero_u8(left ^ right)
2358}
2359
2360#[inline]
2361const fn ct_mask_lt_u8(left: u8, right: u8) -> u8 {
2362 let diff = (left as u16).wrapping_sub(right as u16);
2363 ct_mask_bit((diff >> 8) as u8)
2364}
2365
2366mod backend {
2367 use super::{
2368 Alphabet, DecodeError, EncodeError, checked_encoded_len, decode_padded, decode_unpadded,
2369 encode_base64_value_runtime,
2370 };
2371
2372 pub(super) fn encode_slice<A, const PAD: bool>(
2373 input: &[u8],
2374 output: &mut [u8],
2375 ) -> Result<usize, EncodeError>
2376 where
2377 A: Alphabet,
2378 {
2379 #[cfg(feature = "simd")]
2380 match super::simd::active_backend() {
2381 super::simd::ActiveBackend::Scalar => {}
2382 }
2383
2384 scalar_encode_slice::<A, PAD>(input, output)
2385 }
2386
2387 pub(super) fn decode_slice<A, const PAD: bool>(
2388 input: &[u8],
2389 output: &mut [u8],
2390 ) -> Result<usize, DecodeError>
2391 where
2392 A: Alphabet,
2393 {
2394 #[cfg(feature = "simd")]
2395 match super::simd::active_backend() {
2396 super::simd::ActiveBackend::Scalar => {}
2397 }
2398
2399 scalar_decode_slice::<A, PAD>(input, output)
2400 }
2401
2402 #[cfg(test)]
2403 pub(super) fn scalar_reference_encode_slice<A, const PAD: bool>(
2404 input: &[u8],
2405 output: &mut [u8],
2406 ) -> Result<usize, EncodeError>
2407 where
2408 A: Alphabet,
2409 {
2410 scalar_encode_slice::<A, PAD>(input, output)
2411 }
2412
2413 #[cfg(test)]
2414 pub(super) fn scalar_reference_decode_slice<A, const PAD: bool>(
2415 input: &[u8],
2416 output: &mut [u8],
2417 ) -> Result<usize, DecodeError>
2418 where
2419 A: Alphabet,
2420 {
2421 scalar_decode_slice::<A, PAD>(input, output)
2422 }
2423
2424 fn scalar_encode_slice<A, const PAD: bool>(
2425 input: &[u8],
2426 output: &mut [u8],
2427 ) -> Result<usize, EncodeError>
2428 where
2429 A: Alphabet,
2430 {
2431 let required = checked_encoded_len(input.len(), PAD).ok_or(EncodeError::LengthOverflow)?;
2432 if output.len() < required {
2433 return Err(EncodeError::OutputTooSmall {
2434 required,
2435 available: output.len(),
2436 });
2437 }
2438
2439 let mut read = 0;
2440 let mut write = 0;
2441 while read + 3 <= input.len() {
2442 let b0 = input[read];
2443 let b1 = input[read + 1];
2444 let b2 = input[read + 2];
2445
2446 output[write] = encode_base64_value_runtime::<A>(b0 >> 2);
2447 output[write + 1] =
2448 encode_base64_value_runtime::<A>(((b0 & 0b0000_0011) << 4) | (b1 >> 4));
2449 output[write + 2] =
2450 encode_base64_value_runtime::<A>(((b1 & 0b0000_1111) << 2) | (b2 >> 6));
2451 output[write + 3] = encode_base64_value_runtime::<A>(b2 & 0b0011_1111);
2452
2453 read += 3;
2454 write += 4;
2455 }
2456
2457 match input.len() - read {
2458 0 => {}
2459 1 => {
2460 let b0 = input[read];
2461 output[write] = encode_base64_value_runtime::<A>(b0 >> 2);
2462 output[write + 1] = encode_base64_value_runtime::<A>((b0 & 0b0000_0011) << 4);
2463 write += 2;
2464 if PAD {
2465 output[write] = b'=';
2466 output[write + 1] = b'=';
2467 write += 2;
2468 }
2469 }
2470 2 => {
2471 let b0 = input[read];
2472 let b1 = input[read + 1];
2473 output[write] = encode_base64_value_runtime::<A>(b0 >> 2);
2474 output[write + 1] =
2475 encode_base64_value_runtime::<A>(((b0 & 0b0000_0011) << 4) | (b1 >> 4));
2476 output[write + 2] = encode_base64_value_runtime::<A>((b1 & 0b0000_1111) << 2);
2477 write += 3;
2478 if PAD {
2479 output[write] = b'=';
2480 write += 1;
2481 }
2482 }
2483 _ => unreachable!(),
2484 }
2485
2486 Ok(write)
2487 }
2488
2489 fn scalar_decode_slice<A, const PAD: bool>(
2490 input: &[u8],
2491 output: &mut [u8],
2492 ) -> Result<usize, DecodeError>
2493 where
2494 A: Alphabet,
2495 {
2496 if input.is_empty() {
2497 return Ok(0);
2498 }
2499
2500 if PAD {
2501 decode_padded::<A>(input, output)
2502 } else {
2503 decode_unpadded::<A>(input, output)
2504 }
2505 }
2506}
2507
2508pub struct Engine<A, const PAD: bool> {
2510 alphabet: core::marker::PhantomData<A>,
2511}
2512
2513impl<A, const PAD: bool> Clone for Engine<A, PAD> {
2514 fn clone(&self) -> Self {
2515 *self
2516 }
2517}
2518
2519impl<A, const PAD: bool> Copy for Engine<A, PAD> {}
2520
2521impl<A, const PAD: bool> core::fmt::Debug for Engine<A, PAD> {
2522 fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
2523 formatter
2524 .debug_struct("Engine")
2525 .field("padded", &PAD)
2526 .finish()
2527 }
2528}
2529
2530impl<A, const PAD: bool> Default for Engine<A, PAD> {
2531 fn default() -> Self {
2532 Self {
2533 alphabet: core::marker::PhantomData,
2534 }
2535 }
2536}
2537
2538impl<A, const PAD: bool> Eq for Engine<A, PAD> {}
2539
2540impl<A, const PAD: bool> PartialEq for Engine<A, PAD> {
2541 fn eq(&self, _other: &Self) -> bool {
2542 true
2543 }
2544}
2545
2546impl<A, const PAD: bool> Engine<A, PAD>
2547where
2548 A: Alphabet,
2549{
2550 #[must_use]
2552 pub const fn new() -> Self {
2553 Self {
2554 alphabet: core::marker::PhantomData,
2555 }
2556 }
2557
2558 pub const fn encoded_len(&self, input_len: usize) -> Result<usize, EncodeError> {
2560 encoded_len(input_len, PAD)
2561 }
2562
2563 #[must_use]
2565 pub const fn checked_encoded_len(&self, input_len: usize) -> Option<usize> {
2566 checked_encoded_len(input_len, PAD)
2567 }
2568
2569 pub const fn wrapped_encoded_len(
2574 &self,
2575 input_len: usize,
2576 wrap: LineWrap,
2577 ) -> Result<usize, EncodeError> {
2578 wrapped_encoded_len(input_len, PAD, wrap)
2579 }
2580
2581 pub fn decoded_len(&self, input: &[u8]) -> Result<usize, DecodeError> {
2586 decoded_len(input, PAD)
2587 }
2588
2589 pub fn decoded_len_legacy(&self, input: &[u8]) -> Result<usize, DecodeError> {
2595 validate_legacy_decode::<A, PAD>(input)
2596 }
2597
2598 pub fn decoded_len_wrapped(&self, input: &[u8], wrap: LineWrap) -> Result<usize, DecodeError> {
2605 validate_wrapped_decode::<A, PAD>(input, wrap)
2606 }
2607
2608 pub fn validate_result(&self, input: &[u8]) -> Result<(), DecodeError> {
2623 validate_decode::<A, PAD>(input).map(|_| ())
2624 }
2625
2626 #[must_use]
2639 pub fn validate(&self, input: &[u8]) -> bool {
2640 self.validate_result(input).is_ok()
2641 }
2642
2643 pub fn validate_legacy_result(&self, input: &[u8]) -> Result<(), DecodeError> {
2658 validate_legacy_decode::<A, PAD>(input).map(|_| ())
2659 }
2660
2661 #[must_use]
2675 pub fn validate_legacy(&self, input: &[u8]) -> bool {
2676 self.validate_legacy_result(input).is_ok()
2677 }
2678
2679 pub fn validate_wrapped_result(&self, input: &[u8], wrap: LineWrap) -> Result<(), DecodeError> {
2695 validate_wrapped_decode::<A, PAD>(input, wrap).map(|_| ())
2696 }
2697
2698 #[must_use]
2712 pub fn validate_wrapped(&self, input: &[u8], wrap: LineWrap) -> bool {
2713 self.validate_wrapped_result(input, wrap).is_ok()
2714 }
2715
2716 #[must_use]
2748 pub const fn encode_array<const INPUT_LEN: usize, const OUTPUT_LEN: usize>(
2749 &self,
2750 input: &[u8; INPUT_LEN],
2751 ) -> [u8; OUTPUT_LEN] {
2752 let Some(required) = checked_encoded_len(INPUT_LEN, PAD) else {
2753 panic!("encoded base64 length overflows usize");
2754 };
2755 assert!(
2756 required == OUTPUT_LEN,
2757 "base64 output array has incorrect length"
2758 );
2759
2760 let mut output = [0u8; OUTPUT_LEN];
2761 let mut read = 0;
2762 let mut write = 0;
2763 while INPUT_LEN - read >= 3 {
2764 let b0 = input[read];
2765 let b1 = input[read + 1];
2766 let b2 = input[read + 2];
2767
2768 output[write] = encode_base64_value::<A>(b0 >> 2);
2769 output[write + 1] = encode_base64_value::<A>(((b0 & 0b0000_0011) << 4) | (b1 >> 4));
2770 output[write + 2] = encode_base64_value::<A>(((b1 & 0b0000_1111) << 2) | (b2 >> 6));
2771 output[write + 3] = encode_base64_value::<A>(b2 & 0b0011_1111);
2772
2773 read += 3;
2774 write += 4;
2775 }
2776
2777 match INPUT_LEN - read {
2778 0 => {}
2779 1 => {
2780 let b0 = input[read];
2781 output[write] = encode_base64_value::<A>(b0 >> 2);
2782 output[write + 1] = encode_base64_value::<A>((b0 & 0b0000_0011) << 4);
2783 write += 2;
2784 if PAD {
2785 output[write] = b'=';
2786 output[write + 1] = b'=';
2787 }
2788 }
2789 2 => {
2790 let b0 = input[read];
2791 let b1 = input[read + 1];
2792 output[write] = encode_base64_value::<A>(b0 >> 2);
2793 output[write + 1] = encode_base64_value::<A>(((b0 & 0b0000_0011) << 4) | (b1 >> 4));
2794 output[write + 2] = encode_base64_value::<A>((b1 & 0b0000_1111) << 2);
2795 if PAD {
2796 output[write + 3] = b'=';
2797 }
2798 }
2799 _ => unreachable!(),
2800 }
2801
2802 output
2803 }
2804
2805 pub fn encode_slice(&self, input: &[u8], output: &mut [u8]) -> Result<usize, EncodeError> {
2807 backend::encode_slice::<A, PAD>(input, output)
2808 }
2809
2810 pub fn encode_slice_wrapped(
2829 &self,
2830 input: &[u8],
2831 output: &mut [u8],
2832 wrap: LineWrap,
2833 ) -> Result<usize, EncodeError> {
2834 let required = self.wrapped_encoded_len(input.len(), wrap)?;
2835 if output.len() < required {
2836 return Err(EncodeError::OutputTooSmall {
2837 required,
2838 available: output.len(),
2839 });
2840 }
2841
2842 let encoded_len =
2843 checked_encoded_len(input.len(), PAD).ok_or(EncodeError::LengthOverflow)?;
2844 if encoded_len == 0 {
2845 return Ok(0);
2846 }
2847
2848 if output.len() < required.saturating_add(encoded_len) {
2849 let mut scratch = [0u8; 1024];
2850 let mut input_offset = 0;
2851 let mut output_offset = 0;
2852 let mut column = 0;
2853
2854 while input_offset < input.len() {
2855 let remaining = input.len() - input_offset;
2856 let mut take = remaining.min(768);
2857 if remaining > take {
2858 take -= take % 3;
2859 }
2860 if take == 0 {
2861 take = remaining;
2862 }
2863
2864 let encoded =
2865 self.encode_slice(&input[input_offset..input_offset + take], &mut scratch)?;
2866 write_wrapped_bytes(
2867 &scratch[..encoded],
2868 output,
2869 &mut output_offset,
2870 &mut column,
2871 wrap,
2872 );
2873 wipe_bytes(&mut scratch[..encoded]);
2874 input_offset += take;
2875 }
2876
2877 Ok(output_offset)
2878 } else {
2879 let encoded =
2880 self.encode_slice(input, &mut output[required..required + encoded_len])?;
2881 let mut output_offset = 0;
2882 let mut column = 0;
2883 let mut read = required;
2884 while read < required + encoded {
2885 let byte = output[read];
2886 write_wrapped_byte(byte, output, &mut output_offset, &mut column, wrap);
2887 read += 1;
2888 }
2889 wipe_bytes(&mut output[required..required + encoded]);
2890 Ok(output_offset)
2891 }
2892 }
2893
2894 pub fn encode_slice_wrapped_clear_tail(
2900 &self,
2901 input: &[u8],
2902 output: &mut [u8],
2903 wrap: LineWrap,
2904 ) -> Result<usize, EncodeError> {
2905 let written = match self.encode_slice_wrapped(input, output, wrap) {
2906 Ok(written) => written,
2907 Err(err) => {
2908 wipe_bytes(output);
2909 return Err(err);
2910 }
2911 };
2912 wipe_tail(output, written);
2913 Ok(written)
2914 }
2915
2916 #[cfg(feature = "alloc")]
2918 pub fn encode_wrapped_vec(
2919 &self,
2920 input: &[u8],
2921 wrap: LineWrap,
2922 ) -> Result<alloc::vec::Vec<u8>, EncodeError> {
2923 let required = self.wrapped_encoded_len(input.len(), wrap)?;
2924 let mut output = alloc::vec![0; required];
2925 let written = self.encode_slice_wrapped(input, &mut output, wrap)?;
2926 output.truncate(written);
2927 Ok(output)
2928 }
2929
2930 #[cfg(feature = "alloc")]
2932 pub fn encode_wrapped_string(
2933 &self,
2934 input: &[u8],
2935 wrap: LineWrap,
2936 ) -> Result<alloc::string::String, EncodeError> {
2937 let output = self.encode_wrapped_vec(input, wrap)?;
2938 match alloc::string::String::from_utf8(output) {
2939 Ok(output) => Ok(output),
2940 Err(_) => unreachable!("base64 encoder produced non-UTF-8 output"),
2941 }
2942 }
2943
2944 pub fn encode_slice_clear_tail(
2964 &self,
2965 input: &[u8],
2966 output: &mut [u8],
2967 ) -> Result<usize, EncodeError> {
2968 let written = match self.encode_slice(input, output) {
2969 Ok(written) => written,
2970 Err(err) => {
2971 wipe_bytes(output);
2972 return Err(err);
2973 }
2974 };
2975 wipe_tail(output, written);
2976 Ok(written)
2977 }
2978
2979 pub fn encode_buffer<const CAP: usize>(
2994 &self,
2995 input: &[u8],
2996 ) -> Result<EncodedBuffer<CAP>, EncodeError> {
2997 let mut output = EncodedBuffer::new();
2998 let written = match self.encode_slice_clear_tail(input, &mut output.bytes) {
2999 Ok(written) => written,
3000 Err(err) => {
3001 output.clear();
3002 return Err(err);
3003 }
3004 };
3005 output.len = written;
3006 Ok(output)
3007 }
3008
3009 #[cfg(feature = "alloc")]
3011 pub fn encode_vec(&self, input: &[u8]) -> Result<alloc::vec::Vec<u8>, EncodeError> {
3012 let required = checked_encoded_len(input.len(), PAD).ok_or(EncodeError::LengthOverflow)?;
3013 let mut output = alloc::vec![0; required];
3014 let written = self.encode_slice(input, &mut output)?;
3015 output.truncate(written);
3016 Ok(output)
3017 }
3018
3019 #[cfg(feature = "alloc")]
3024 pub fn encode_secret(&self, input: &[u8]) -> Result<SecretBuffer, EncodeError> {
3025 self.encode_vec(input).map(SecretBuffer::from_vec)
3026 }
3027
3028 #[cfg(feature = "alloc")]
3043 pub fn encode_string(&self, input: &[u8]) -> Result<alloc::string::String, EncodeError> {
3044 let output = self.encode_vec(input)?;
3045 match alloc::string::String::from_utf8(output) {
3046 Ok(output) => Ok(output),
3047 Err(_) => unreachable!("base64 encoder produced non-UTF-8 output"),
3048 }
3049 }
3050
3051 pub fn encode_in_place<'a>(
3068 &self,
3069 buffer: &'a mut [u8],
3070 input_len: usize,
3071 ) -> Result<&'a mut [u8], EncodeError> {
3072 if input_len > buffer.len() {
3073 return Err(EncodeError::InputTooLarge {
3074 input_len,
3075 buffer_len: buffer.len(),
3076 });
3077 }
3078
3079 let required = checked_encoded_len(input_len, PAD).ok_or(EncodeError::LengthOverflow)?;
3080 if buffer.len() < required {
3081 return Err(EncodeError::OutputTooSmall {
3082 required,
3083 available: buffer.len(),
3084 });
3085 }
3086
3087 let mut read = input_len;
3088 let mut write = required;
3089
3090 match input_len % 3 {
3091 0 => {}
3092 1 => {
3093 read -= 1;
3094 let b0 = buffer[read];
3095 if PAD {
3096 write -= 4;
3097 buffer[write] = encode_base64_value_runtime::<A>(b0 >> 2);
3098 buffer[write + 1] = encode_base64_value_runtime::<A>((b0 & 0b0000_0011) << 4);
3099 buffer[write + 2] = b'=';
3100 buffer[write + 3] = b'=';
3101 } else {
3102 write -= 2;
3103 buffer[write] = encode_base64_value_runtime::<A>(b0 >> 2);
3104 buffer[write + 1] = encode_base64_value_runtime::<A>((b0 & 0b0000_0011) << 4);
3105 }
3106 }
3107 2 => {
3108 read -= 2;
3109 let b0 = buffer[read];
3110 let b1 = buffer[read + 1];
3111 if PAD {
3112 write -= 4;
3113 buffer[write] = encode_base64_value_runtime::<A>(b0 >> 2);
3114 buffer[write + 1] =
3115 encode_base64_value_runtime::<A>(((b0 & 0b0000_0011) << 4) | (b1 >> 4));
3116 buffer[write + 2] = encode_base64_value_runtime::<A>((b1 & 0b0000_1111) << 2);
3117 buffer[write + 3] = b'=';
3118 } else {
3119 write -= 3;
3120 buffer[write] = encode_base64_value_runtime::<A>(b0 >> 2);
3121 buffer[write + 1] =
3122 encode_base64_value_runtime::<A>(((b0 & 0b0000_0011) << 4) | (b1 >> 4));
3123 buffer[write + 2] = encode_base64_value_runtime::<A>((b1 & 0b0000_1111) << 2);
3124 }
3125 }
3126 _ => unreachable!(),
3127 }
3128
3129 while read > 0 {
3130 read -= 3;
3131 write -= 4;
3132 let b0 = buffer[read];
3133 let b1 = buffer[read + 1];
3134 let b2 = buffer[read + 2];
3135
3136 buffer[write] = encode_base64_value_runtime::<A>(b0 >> 2);
3137 buffer[write + 1] =
3138 encode_base64_value_runtime::<A>(((b0 & 0b0000_0011) << 4) | (b1 >> 4));
3139 buffer[write + 2] =
3140 encode_base64_value_runtime::<A>(((b1 & 0b0000_1111) << 2) | (b2 >> 6));
3141 buffer[write + 3] = encode_base64_value_runtime::<A>(b2 & 0b0011_1111);
3142 }
3143
3144 debug_assert_eq!(write, 0);
3145 Ok(&mut buffer[..required])
3146 }
3147
3148 pub fn encode_in_place_clear_tail<'a>(
3166 &self,
3167 buffer: &'a mut [u8],
3168 input_len: usize,
3169 ) -> Result<&'a mut [u8], EncodeError> {
3170 let len = match self.encode_in_place(buffer, input_len) {
3171 Ok(encoded) => encoded.len(),
3172 Err(err) => {
3173 wipe_bytes(buffer);
3174 return Err(err);
3175 }
3176 };
3177 wipe_tail(buffer, len);
3178 Ok(&mut buffer[..len])
3179 }
3180
3181 pub fn decode_slice(&self, input: &[u8], output: &mut [u8]) -> Result<usize, DecodeError> {
3186 backend::decode_slice::<A, PAD>(input, output)
3187 }
3188
3189 pub fn decode_slice_clear_tail(
3209 &self,
3210 input: &[u8],
3211 output: &mut [u8],
3212 ) -> Result<usize, DecodeError> {
3213 let written = match self.decode_slice(input, output) {
3214 Ok(written) => written,
3215 Err(err) => {
3216 wipe_bytes(output);
3217 return Err(err);
3218 }
3219 };
3220 wipe_tail(output, written);
3221 Ok(written)
3222 }
3223
3224 pub fn decode_slice_legacy(
3230 &self,
3231 input: &[u8],
3232 output: &mut [u8],
3233 ) -> Result<usize, DecodeError> {
3234 let required = validate_legacy_decode::<A, PAD>(input)?;
3235 if output.len() < required {
3236 return Err(DecodeError::OutputTooSmall {
3237 required,
3238 available: output.len(),
3239 });
3240 }
3241 decode_legacy_to_slice::<A, PAD>(input, output)
3242 }
3243
3244 pub fn decode_slice_legacy_clear_tail(
3264 &self,
3265 input: &[u8],
3266 output: &mut [u8],
3267 ) -> Result<usize, DecodeError> {
3268 let written = match self.decode_slice_legacy(input, output) {
3269 Ok(written) => written,
3270 Err(err) => {
3271 wipe_bytes(output);
3272 return Err(err);
3273 }
3274 };
3275 wipe_tail(output, written);
3276 Ok(written)
3277 }
3278
3279 pub fn decode_slice_wrapped(
3286 &self,
3287 input: &[u8],
3288 output: &mut [u8],
3289 wrap: LineWrap,
3290 ) -> Result<usize, DecodeError> {
3291 let required = validate_wrapped_decode::<A, PAD>(input, wrap)?;
3292 if output.len() < required {
3293 return Err(DecodeError::OutputTooSmall {
3294 required,
3295 available: output.len(),
3296 });
3297 }
3298 decode_wrapped_to_slice::<A, PAD>(input, output, wrap)
3299 }
3300
3301 pub fn decode_slice_wrapped_clear_tail(
3307 &self,
3308 input: &[u8],
3309 output: &mut [u8],
3310 wrap: LineWrap,
3311 ) -> Result<usize, DecodeError> {
3312 let written = match self.decode_slice_wrapped(input, output, wrap) {
3313 Ok(written) => written,
3314 Err(err) => {
3315 wipe_bytes(output);
3316 return Err(err);
3317 }
3318 };
3319 wipe_tail(output, written);
3320 Ok(written)
3321 }
3322
3323 #[cfg(feature = "alloc")]
3327 pub fn decode_vec(&self, input: &[u8]) -> Result<alloc::vec::Vec<u8>, DecodeError> {
3328 let required = validate_decode::<A, PAD>(input)?;
3329 let mut output = alloc::vec![0; required];
3330 let written = match self.decode_slice(input, &mut output) {
3331 Ok(written) => written,
3332 Err(err) => {
3333 wipe_bytes(&mut output);
3334 return Err(err);
3335 }
3336 };
3337 output.truncate(written);
3338 Ok(output)
3339 }
3340
3341 #[cfg(feature = "alloc")]
3346 pub fn decode_secret(&self, input: &[u8]) -> Result<SecretBuffer, DecodeError> {
3347 self.decode_vec(input).map(SecretBuffer::from_vec)
3348 }
3349
3350 #[cfg(feature = "alloc")]
3353 pub fn decode_vec_legacy(&self, input: &[u8]) -> Result<alloc::vec::Vec<u8>, DecodeError> {
3354 let required = validate_legacy_decode::<A, PAD>(input)?;
3355 let mut output = alloc::vec![0; required];
3356 let written = match self.decode_slice_legacy(input, &mut output) {
3357 Ok(written) => written,
3358 Err(err) => {
3359 wipe_bytes(&mut output);
3360 return Err(err);
3361 }
3362 };
3363 output.truncate(written);
3364 Ok(output)
3365 }
3366
3367 #[cfg(feature = "alloc")]
3369 pub fn decode_wrapped_vec(
3370 &self,
3371 input: &[u8],
3372 wrap: LineWrap,
3373 ) -> Result<alloc::vec::Vec<u8>, DecodeError> {
3374 let required = validate_wrapped_decode::<A, PAD>(input, wrap)?;
3375 let mut output = alloc::vec![0; required];
3376 let written = match self.decode_slice_wrapped(input, &mut output, wrap) {
3377 Ok(written) => written,
3378 Err(err) => {
3379 wipe_bytes(&mut output);
3380 return Err(err);
3381 }
3382 };
3383 output.truncate(written);
3384 Ok(output)
3385 }
3386
3387 pub fn decode_in_place<'a>(&self, buffer: &'a mut [u8]) -> Result<&'a mut [u8], DecodeError> {
3399 let len = Self::decode_slice_to_start(buffer)?;
3400 Ok(&mut buffer[..len])
3401 }
3402
3403 pub fn decode_in_place_clear_tail<'a>(
3420 &self,
3421 buffer: &'a mut [u8],
3422 ) -> Result<&'a mut [u8], DecodeError> {
3423 let len = match Self::decode_slice_to_start(buffer) {
3424 Ok(len) => len,
3425 Err(err) => {
3426 wipe_bytes(buffer);
3427 return Err(err);
3428 }
3429 };
3430 wipe_tail(buffer, len);
3431 Ok(&mut buffer[..len])
3432 }
3433
3434 pub fn decode_in_place_legacy<'a>(
3439 &self,
3440 buffer: &'a mut [u8],
3441 ) -> Result<&'a mut [u8], DecodeError> {
3442 let _required = validate_legacy_decode::<A, PAD>(buffer)?;
3443 let mut write = 0;
3444 let mut read = 0;
3445 while read < buffer.len() {
3446 let byte = buffer[read];
3447 if !is_legacy_whitespace(byte) {
3448 buffer[write] = byte;
3449 write += 1;
3450 }
3451 read += 1;
3452 }
3453 let len = Self::decode_slice_to_start(&mut buffer[..write])?;
3454 Ok(&mut buffer[..len])
3455 }
3456
3457 pub fn decode_in_place_legacy_clear_tail<'a>(
3463 &self,
3464 buffer: &'a mut [u8],
3465 ) -> Result<&'a mut [u8], DecodeError> {
3466 if let Err(err) = validate_legacy_decode::<A, PAD>(buffer) {
3467 wipe_bytes(buffer);
3468 return Err(err);
3469 }
3470
3471 let mut write = 0;
3472 let mut read = 0;
3473 while read < buffer.len() {
3474 let byte = buffer[read];
3475 if !is_legacy_whitespace(byte) {
3476 buffer[write] = byte;
3477 write += 1;
3478 }
3479 read += 1;
3480 }
3481
3482 let len = match Self::decode_slice_to_start(&mut buffer[..write]) {
3483 Ok(len) => len,
3484 Err(err) => {
3485 wipe_bytes(buffer);
3486 return Err(err);
3487 }
3488 };
3489 wipe_tail(buffer, len);
3490 Ok(&mut buffer[..len])
3491 }
3492
3493 fn decode_slice_to_start(buffer: &mut [u8]) -> Result<usize, DecodeError> {
3494 let input_len = buffer.len();
3495 let mut read = 0;
3496 let mut write = 0;
3497 while read + 4 <= input_len {
3498 let chunk = read_quad(buffer, read)?;
3499 let available = buffer.len();
3500 let output_tail = buffer.get_mut(write..).ok_or(DecodeError::OutputTooSmall {
3501 required: write,
3502 available,
3503 })?;
3504 let written = decode_chunk::<A, PAD>(chunk, output_tail)
3505 .map_err(|err| err.with_index_offset(read))?;
3506 read += 4;
3507 write += written;
3508 if written < 3 {
3509 if read != input_len {
3510 return Err(DecodeError::InvalidPadding { index: read - 4 });
3511 }
3512 return Ok(write);
3513 }
3514 }
3515
3516 let rem = input_len - read;
3517 if rem == 0 {
3518 return Ok(write);
3519 }
3520 if PAD {
3521 return Err(DecodeError::InvalidLength);
3522 }
3523 let mut tail = [0u8; 3];
3524 tail[..rem].copy_from_slice(&buffer[read..input_len]);
3525 decode_tail_unpadded::<A>(&tail[..rem], &mut buffer[write..])
3526 .map_err(|err| err.with_index_offset(read))
3527 .map(|n| write + n)
3528 }
3529}
3530
3531fn write_wrapped_bytes(
3532 input: &[u8],
3533 output: &mut [u8],
3534 output_offset: &mut usize,
3535 column: &mut usize,
3536 wrap: LineWrap,
3537) {
3538 for byte in input {
3539 write_wrapped_byte(*byte, output, output_offset, column, wrap);
3540 }
3541}
3542
3543fn write_wrapped_byte(
3544 byte: u8,
3545 output: &mut [u8],
3546 output_offset: &mut usize,
3547 column: &mut usize,
3548 wrap: LineWrap,
3549) {
3550 if *column == wrap.line_len {
3551 let line_ending = wrap.line_ending.as_bytes();
3552 let mut index = 0;
3553 while index < line_ending.len() {
3554 output[*output_offset] = line_ending[index];
3555 *output_offset += 1;
3556 index += 1;
3557 }
3558 *column = 0;
3559 }
3560
3561 output[*output_offset] = byte;
3562 *output_offset += 1;
3563 *column += 1;
3564}
3565
3566#[derive(Clone, Copy, Debug, Eq, PartialEq)]
3568pub enum EncodeError {
3569 LengthOverflow,
3571 InvalidLineWrap {
3573 line_len: usize,
3575 },
3576 InputTooLarge {
3578 input_len: usize,
3580 buffer_len: usize,
3582 },
3583 OutputTooSmall {
3585 required: usize,
3587 available: usize,
3589 },
3590}
3591
3592impl core::fmt::Display for EncodeError {
3593 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
3594 match self {
3595 Self::LengthOverflow => f.write_str("base64 output length overflows usize"),
3596 Self::InvalidLineWrap { line_len } => {
3597 write!(f, "base64 line wrap length {line_len} is invalid")
3598 }
3599 Self::InputTooLarge {
3600 input_len,
3601 buffer_len,
3602 } => write!(
3603 f,
3604 "base64 input length {input_len} exceeds buffer length {buffer_len}"
3605 ),
3606 Self::OutputTooSmall {
3607 required,
3608 available,
3609 } => write!(
3610 f,
3611 "base64 output buffer too small: required {required}, available {available}"
3612 ),
3613 }
3614 }
3615}
3616
3617#[cfg(feature = "std")]
3618impl std::error::Error for EncodeError {}
3619
3620#[derive(Clone, Copy, Debug, Eq, PartialEq)]
3622pub enum AlphabetError {
3623 InvalidByte {
3625 index: usize,
3627 byte: u8,
3629 },
3630 PaddingByte {
3632 index: usize,
3634 },
3635 DuplicateByte {
3637 first: usize,
3639 second: usize,
3641 byte: u8,
3643 },
3644}
3645
3646impl core::fmt::Display for AlphabetError {
3647 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
3648 match self {
3649 Self::InvalidByte { index, byte } => {
3650 write!(
3651 f,
3652 "invalid base64 alphabet byte 0x{byte:02x} at index {index}"
3653 )
3654 }
3655 Self::PaddingByte { index } => {
3656 write!(f, "base64 alphabet contains padding byte at index {index}")
3657 }
3658 Self::DuplicateByte {
3659 first,
3660 second,
3661 byte,
3662 } => write!(
3663 f,
3664 "base64 alphabet byte 0x{byte:02x} is duplicated at indexes {first} and {second}"
3665 ),
3666 }
3667 }
3668}
3669
3670#[cfg(feature = "std")]
3671impl std::error::Error for AlphabetError {}
3672
3673#[derive(Clone, Copy, Debug, Eq, PartialEq)]
3675pub enum DecodeError {
3676 InvalidInput,
3679 InvalidLength,
3681 InvalidByte {
3683 index: usize,
3685 byte: u8,
3687 },
3688 InvalidPadding {
3690 index: usize,
3692 },
3693 InvalidLineWrap {
3695 index: usize,
3697 },
3698 OutputTooSmall {
3700 required: usize,
3702 available: usize,
3704 },
3705}
3706
3707impl core::fmt::Display for DecodeError {
3708 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
3709 match self {
3710 Self::InvalidInput => f.write_str("malformed base64 input"),
3711 Self::InvalidLength => f.write_str("invalid base64 input length"),
3712 Self::InvalidByte { index, byte } => {
3713 write!(f, "invalid base64 byte 0x{byte:02x} at index {index}")
3714 }
3715 Self::InvalidPadding { index } => write!(f, "invalid base64 padding at index {index}"),
3716 Self::InvalidLineWrap { index } => {
3717 write!(f, "invalid base64 line wrapping at index {index}")
3718 }
3719 Self::OutputTooSmall {
3720 required,
3721 available,
3722 } => write!(
3723 f,
3724 "base64 decode output buffer too small: required {required}, available {available}"
3725 ),
3726 }
3727 }
3728}
3729
3730impl DecodeError {
3731 fn with_index_offset(self, offset: usize) -> Self {
3732 match self {
3733 Self::InvalidByte { index, byte } => Self::InvalidByte {
3734 index: index + offset,
3735 byte,
3736 },
3737 Self::InvalidPadding { index } => Self::InvalidPadding {
3738 index: index + offset,
3739 },
3740 Self::InvalidLineWrap { index } => Self::InvalidLineWrap {
3741 index: index + offset,
3742 },
3743 Self::InvalidInput | Self::InvalidLength | Self::OutputTooSmall { .. } => self,
3744 }
3745 }
3746}
3747
3748#[cfg(feature = "std")]
3749impl std::error::Error for DecodeError {}
3750
3751fn validate_legacy_decode<A: Alphabet, const PAD: bool>(
3752 input: &[u8],
3753) -> Result<usize, DecodeError> {
3754 let mut chunk = [0u8; 4];
3755 let mut indexes = [0usize; 4];
3756 let mut chunk_len = 0;
3757 let mut required = 0;
3758 let mut terminal_seen = false;
3759
3760 for (index, byte) in input.iter().copied().enumerate() {
3761 if is_legacy_whitespace(byte) {
3762 continue;
3763 }
3764 if terminal_seen {
3765 return Err(DecodeError::InvalidPadding { index });
3766 }
3767
3768 chunk[chunk_len] = byte;
3769 indexes[chunk_len] = index;
3770 chunk_len += 1;
3771
3772 if chunk_len == 4 {
3773 let written =
3774 validate_chunk::<A, PAD>(chunk).map_err(|err| map_chunk_error(err, &indexes))?;
3775 required += written;
3776 terminal_seen = written < 3;
3777 chunk_len = 0;
3778 }
3779 }
3780
3781 if chunk_len == 0 {
3782 return Ok(required);
3783 }
3784 if PAD {
3785 return Err(DecodeError::InvalidLength);
3786 }
3787
3788 validate_tail_unpadded::<A>(&chunk[..chunk_len])
3789 .map_err(|err| map_partial_chunk_error(err, &indexes, chunk_len))?;
3790 Ok(required + decoded_capacity(chunk_len))
3791}
3792
3793fn decode_legacy_to_slice<A: Alphabet, const PAD: bool>(
3794 input: &[u8],
3795 output: &mut [u8],
3796) -> Result<usize, DecodeError> {
3797 let mut chunk = [0u8; 4];
3798 let mut indexes = [0usize; 4];
3799 let mut chunk_len = 0;
3800 let mut write = 0;
3801 let mut terminal_seen = false;
3802
3803 for (index, byte) in input.iter().copied().enumerate() {
3804 if is_legacy_whitespace(byte) {
3805 continue;
3806 }
3807 if terminal_seen {
3808 return Err(DecodeError::InvalidPadding { index });
3809 }
3810
3811 chunk[chunk_len] = byte;
3812 indexes[chunk_len] = index;
3813 chunk_len += 1;
3814
3815 if chunk_len == 4 {
3816 let available = output.len();
3817 let output_tail = output.get_mut(write..).ok_or(DecodeError::OutputTooSmall {
3818 required: write,
3819 available,
3820 })?;
3821 let written = decode_chunk::<A, PAD>(chunk, output_tail)
3822 .map_err(|err| map_chunk_error(err, &indexes))?;
3823 write += written;
3824 terminal_seen = written < 3;
3825 chunk_len = 0;
3826 }
3827 }
3828
3829 if chunk_len == 0 {
3830 return Ok(write);
3831 }
3832 if PAD {
3833 return Err(DecodeError::InvalidLength);
3834 }
3835
3836 decode_tail_unpadded::<A>(&chunk[..chunk_len], &mut output[write..])
3837 .map_err(|err| map_partial_chunk_error(err, &indexes, chunk_len))
3838 .map(|n| write + n)
3839}
3840
3841struct WrappedBytes<'a> {
3842 input: &'a [u8],
3843 wrap: LineWrap,
3844 index: usize,
3845 line_len: usize,
3846}
3847
3848impl<'a> WrappedBytes<'a> {
3849 const fn new(input: &'a [u8], wrap: LineWrap) -> Result<Self, DecodeError> {
3850 if wrap.line_len == 0 {
3851 return Err(DecodeError::InvalidLineWrap { index: 0 });
3852 }
3853 Ok(Self {
3854 input,
3855 wrap,
3856 index: 0,
3857 line_len: 0,
3858 })
3859 }
3860
3861 fn next_byte(&mut self) -> Result<Option<(usize, u8)>, DecodeError> {
3862 loop {
3863 if self.index == self.input.len() {
3864 return Ok(None);
3865 }
3866
3867 if self.starts_with_line_ending() {
3868 let line_end_index = self.index;
3869 if self.line_len == 0 {
3870 return Err(DecodeError::InvalidLineWrap {
3871 index: line_end_index,
3872 });
3873 }
3874
3875 self.index += self.wrap.line_ending.byte_len();
3876 if self.index == self.input.len() {
3877 self.line_len = 0;
3878 return Ok(None);
3879 }
3880
3881 if self.line_len != self.wrap.line_len {
3882 return Err(DecodeError::InvalidLineWrap {
3883 index: line_end_index,
3884 });
3885 }
3886 self.line_len = 0;
3887 continue;
3888 }
3889
3890 let byte = self.input[self.index];
3891 if matches!(byte, b'\r' | b'\n') {
3892 return Err(DecodeError::InvalidLineWrap { index: self.index });
3893 }
3894
3895 self.line_len += 1;
3896 if self.line_len > self.wrap.line_len {
3897 return Err(DecodeError::InvalidLineWrap { index: self.index });
3898 }
3899
3900 let index = self.index;
3901 self.index += 1;
3902 return Ok(Some((index, byte)));
3903 }
3904 }
3905
3906 fn starts_with_line_ending(&self) -> bool {
3907 let line_ending = self.wrap.line_ending.as_bytes();
3908 let end = self.index + line_ending.len();
3909 end <= self.input.len() && &self.input[self.index..end] == line_ending
3910 }
3911}
3912
3913fn validate_wrapped_decode<A: Alphabet, const PAD: bool>(
3914 input: &[u8],
3915 wrap: LineWrap,
3916) -> Result<usize, DecodeError> {
3917 let mut bytes = WrappedBytes::new(input, wrap)?;
3918 let mut chunk = [0u8; 4];
3919 let mut indexes = [0usize; 4];
3920 let mut chunk_len = 0;
3921 let mut required = 0;
3922 let mut terminal_seen = false;
3923
3924 while let Some((index, byte)) = bytes.next_byte()? {
3925 if terminal_seen {
3926 return Err(DecodeError::InvalidPadding { index });
3927 }
3928
3929 chunk[chunk_len] = byte;
3930 indexes[chunk_len] = index;
3931 chunk_len += 1;
3932
3933 if chunk_len == 4 {
3934 let written =
3935 validate_chunk::<A, PAD>(chunk).map_err(|err| map_chunk_error(err, &indexes))?;
3936 required += written;
3937 terminal_seen = written < 3;
3938 chunk_len = 0;
3939 }
3940 }
3941
3942 if chunk_len == 0 {
3943 return Ok(required);
3944 }
3945 if PAD {
3946 return Err(DecodeError::InvalidLength);
3947 }
3948
3949 validate_tail_unpadded::<A>(&chunk[..chunk_len])
3950 .map_err(|err| map_partial_chunk_error(err, &indexes, chunk_len))?;
3951 Ok(required + decoded_capacity(chunk_len))
3952}
3953
3954fn decode_wrapped_to_slice<A: Alphabet, const PAD: bool>(
3955 input: &[u8],
3956 output: &mut [u8],
3957 wrap: LineWrap,
3958) -> Result<usize, DecodeError> {
3959 let mut bytes = WrappedBytes::new(input, wrap)?;
3960 let mut chunk = [0u8; 4];
3961 let mut indexes = [0usize; 4];
3962 let mut chunk_len = 0;
3963 let mut write = 0;
3964 let mut terminal_seen = false;
3965
3966 while let Some((index, byte)) = bytes.next_byte()? {
3967 if terminal_seen {
3968 return Err(DecodeError::InvalidPadding { index });
3969 }
3970
3971 chunk[chunk_len] = byte;
3972 indexes[chunk_len] = index;
3973 chunk_len += 1;
3974
3975 if chunk_len == 4 {
3976 let available = output.len();
3977 let output_tail = output.get_mut(write..).ok_or(DecodeError::OutputTooSmall {
3978 required: write,
3979 available,
3980 })?;
3981 let written = decode_chunk::<A, PAD>(chunk, output_tail)
3982 .map_err(|err| map_chunk_error(err, &indexes))?;
3983 write += written;
3984 terminal_seen = written < 3;
3985 chunk_len = 0;
3986 }
3987 }
3988
3989 if chunk_len == 0 {
3990 return Ok(write);
3991 }
3992 if PAD {
3993 return Err(DecodeError::InvalidLength);
3994 }
3995
3996 decode_tail_unpadded::<A>(&chunk[..chunk_len], &mut output[write..])
3997 .map_err(|err| map_partial_chunk_error(err, &indexes, chunk_len))
3998 .map(|n| write + n)
3999}
4000
4001#[inline]
4002const fn is_legacy_whitespace(byte: u8) -> bool {
4003 matches!(byte, b' ' | b'\t' | b'\r' | b'\n')
4004}
4005
4006fn map_chunk_error(err: DecodeError, indexes: &[usize; 4]) -> DecodeError {
4007 match err {
4008 DecodeError::InvalidByte { index, byte } => DecodeError::InvalidByte {
4009 index: indexes[index],
4010 byte,
4011 },
4012 DecodeError::InvalidPadding { index } => DecodeError::InvalidPadding {
4013 index: indexes[index],
4014 },
4015 DecodeError::InvalidInput
4016 | DecodeError::InvalidLineWrap { .. }
4017 | DecodeError::InvalidLength
4018 | DecodeError::OutputTooSmall { .. } => err,
4019 }
4020}
4021
4022fn map_partial_chunk_error(err: DecodeError, indexes: &[usize; 4], len: usize) -> DecodeError {
4023 match err {
4024 DecodeError::InvalidByte { index, byte } if index < len => DecodeError::InvalidByte {
4025 index: indexes[index],
4026 byte,
4027 },
4028 DecodeError::InvalidPadding { index } if index < len => DecodeError::InvalidPadding {
4029 index: indexes[index],
4030 },
4031 DecodeError::InvalidByte { .. }
4032 | DecodeError::InvalidPadding { .. }
4033 | DecodeError::InvalidLineWrap { .. }
4034 | DecodeError::InvalidInput
4035 | DecodeError::InvalidLength
4036 | DecodeError::OutputTooSmall { .. } => err,
4037 }
4038}
4039
4040fn decode_padded<A: Alphabet>(input: &[u8], output: &mut [u8]) -> Result<usize, DecodeError> {
4041 if !input.len().is_multiple_of(4) {
4042 return Err(DecodeError::InvalidLength);
4043 }
4044 let required = decoded_len_padded(input)?;
4045 if output.len() < required {
4046 return Err(DecodeError::OutputTooSmall {
4047 required,
4048 available: output.len(),
4049 });
4050 }
4051
4052 let mut read = 0;
4053 let mut write = 0;
4054 while read < input.len() {
4055 let chunk = read_quad(input, read)?;
4056 let available = output.len();
4057 let output_tail = output.get_mut(write..).ok_or(DecodeError::OutputTooSmall {
4058 required: write,
4059 available,
4060 })?;
4061 let written = decode_chunk::<A, true>(chunk, output_tail)
4062 .map_err(|err| err.with_index_offset(read))?;
4063 read += 4;
4064 write += written;
4065 if written < 3 && read != input.len() {
4066 return Err(DecodeError::InvalidPadding { index: read - 4 });
4067 }
4068 }
4069 Ok(write)
4070}
4071
4072fn validate_decode<A: Alphabet, const PAD: bool>(input: &[u8]) -> Result<usize, DecodeError> {
4073 if input.is_empty() {
4074 return Ok(0);
4075 }
4076
4077 if PAD {
4078 validate_padded::<A>(input)
4079 } else {
4080 validate_unpadded::<A>(input)
4081 }
4082}
4083
4084fn validate_padded<A: Alphabet>(input: &[u8]) -> Result<usize, DecodeError> {
4085 if !input.len().is_multiple_of(4) {
4086 return Err(DecodeError::InvalidLength);
4087 }
4088 let required = decoded_len_padded(input)?;
4089
4090 let mut read = 0;
4091 while read < input.len() {
4092 let chunk = read_quad(input, read)?;
4093 let written =
4094 validate_chunk::<A, true>(chunk).map_err(|err| err.with_index_offset(read))?;
4095 read += 4;
4096 if written < 3 && read != input.len() {
4097 return Err(DecodeError::InvalidPadding { index: read - 4 });
4098 }
4099 }
4100
4101 Ok(required)
4102}
4103
4104fn validate_unpadded<A: Alphabet>(input: &[u8]) -> Result<usize, DecodeError> {
4105 let required = decoded_len_unpadded(input)?;
4106
4107 let mut read = 0;
4108 while read + 4 <= input.len() {
4109 let chunk = read_quad(input, read)?;
4110 validate_chunk::<A, false>(chunk).map_err(|err| err.with_index_offset(read))?;
4111 read += 4;
4112 }
4113 validate_tail_unpadded::<A>(&input[read..]).map_err(|err| err.with_index_offset(read))?;
4114
4115 Ok(required)
4116}
4117
4118fn decode_unpadded<A: Alphabet>(input: &[u8], output: &mut [u8]) -> Result<usize, DecodeError> {
4119 let required = decoded_len_unpadded(input)?;
4120 if output.len() < required {
4121 return Err(DecodeError::OutputTooSmall {
4122 required,
4123 available: output.len(),
4124 });
4125 }
4126
4127 let mut read = 0;
4128 let mut write = 0;
4129 while read + 4 <= input.len() {
4130 let chunk = read_quad(input, read)?;
4131 let available = output.len();
4132 let output_tail = output.get_mut(write..).ok_or(DecodeError::OutputTooSmall {
4133 required: write,
4134 available,
4135 })?;
4136 let written = decode_chunk::<A, false>(chunk, output_tail)
4137 .map_err(|err| err.with_index_offset(read))?;
4138 read += 4;
4139 write += written;
4140 }
4141 decode_tail_unpadded::<A>(&input[read..], &mut output[write..])
4142 .map_err(|err| err.with_index_offset(read))
4143 .map(|n| write + n)
4144}
4145
4146fn decoded_len_padded(input: &[u8]) -> Result<usize, DecodeError> {
4147 if input.is_empty() {
4148 return Ok(0);
4149 }
4150 if !input.len().is_multiple_of(4) {
4151 return Err(DecodeError::InvalidLength);
4152 }
4153 let mut padding = 0;
4154 if input[input.len() - 1] == b'=' {
4155 padding += 1;
4156 }
4157 if input[input.len() - 2] == b'=' {
4158 padding += 1;
4159 }
4160 if padding == 0
4161 && let Some(index) = input.iter().position(|byte| *byte == b'=')
4162 {
4163 return Err(DecodeError::InvalidPadding { index });
4164 }
4165 if padding > 0 {
4166 let first_pad = input.len() - padding;
4167 if let Some(index) = input[..first_pad].iter().position(|byte| *byte == b'=') {
4168 return Err(DecodeError::InvalidPadding { index });
4169 }
4170 }
4171 Ok(input.len() / 4 * 3 - padding)
4172}
4173
4174fn decoded_len_unpadded(input: &[u8]) -> Result<usize, DecodeError> {
4175 if input.len() % 4 == 1 {
4176 return Err(DecodeError::InvalidLength);
4177 }
4178 if let Some(index) = input.iter().position(|byte| *byte == b'=') {
4179 return Err(DecodeError::InvalidPadding { index });
4180 }
4181 Ok(decoded_capacity(input.len()))
4182}
4183
4184fn read_quad(input: &[u8], offset: usize) -> Result<[u8; 4], DecodeError> {
4185 let end = offset.checked_add(4).ok_or(DecodeError::InvalidLength)?;
4186 match input.get(offset..end) {
4187 Some([b0, b1, b2, b3]) => Ok([*b0, *b1, *b2, *b3]),
4188 _ => Err(DecodeError::InvalidLength),
4189 }
4190}
4191
4192fn first_padding_index(input: [u8; 4]) -> usize {
4193 let [b0, b1, b2, b3] = input;
4194 if b0 == b'=' {
4195 0
4196 } else if b1 == b'=' {
4197 1
4198 } else if b2 == b'=' {
4199 2
4200 } else if b3 == b'=' {
4201 3
4202 } else {
4203 0
4204 }
4205}
4206
4207fn validate_chunk<A: Alphabet, const PAD: bool>(input: [u8; 4]) -> Result<usize, DecodeError> {
4208 let [b0, b1, b2, b3] = input;
4209 let _v0 = decode_byte::<A>(b0, 0)?;
4210 let v1 = decode_byte::<A>(b1, 1)?;
4211
4212 match (b2, b3) {
4213 (b'=', b'=') if PAD => {
4214 if v1 & 0b0000_1111 != 0 {
4215 return Err(DecodeError::InvalidPadding { index: 1 });
4216 }
4217 Ok(1)
4218 }
4219 (b'=', _) if PAD => Err(DecodeError::InvalidPadding { index: 2 }),
4220 (_, b'=') if PAD => {
4221 let v2 = decode_byte::<A>(b2, 2)?;
4222 if v2 & 0b0000_0011 != 0 {
4223 return Err(DecodeError::InvalidPadding { index: 2 });
4224 }
4225 Ok(2)
4226 }
4227 (b'=', _) | (_, b'=') => Err(DecodeError::InvalidPadding {
4228 index: first_padding_index(input),
4229 }),
4230 _ => {
4231 decode_byte::<A>(b2, 2)?;
4232 decode_byte::<A>(b3, 3)?;
4233 Ok(3)
4234 }
4235 }
4236}
4237
4238fn decode_chunk<A: Alphabet, const PAD: bool>(
4239 input: [u8; 4],
4240 output: &mut [u8],
4241) -> Result<usize, DecodeError> {
4242 let [b0, b1, b2, b3] = input;
4243 let v0 = decode_byte::<A>(b0, 0)?;
4244 let v1 = decode_byte::<A>(b1, 1)?;
4245
4246 match (b2, b3) {
4247 (b'=', b'=') if PAD => {
4248 if output.is_empty() {
4249 return Err(DecodeError::OutputTooSmall {
4250 required: 1,
4251 available: output.len(),
4252 });
4253 }
4254 if v1 & 0b0000_1111 != 0 {
4255 return Err(DecodeError::InvalidPadding { index: 1 });
4256 }
4257 output[0] = (v0 << 2) | (v1 >> 4);
4258 Ok(1)
4259 }
4260 (b'=', _) if PAD => Err(DecodeError::InvalidPadding { index: 2 }),
4261 (_, b'=') if PAD => {
4262 if output.len() < 2 {
4263 return Err(DecodeError::OutputTooSmall {
4264 required: 2,
4265 available: output.len(),
4266 });
4267 }
4268 let v2 = decode_byte::<A>(b2, 2)?;
4269 if v2 & 0b0000_0011 != 0 {
4270 return Err(DecodeError::InvalidPadding { index: 2 });
4271 }
4272 output[0] = (v0 << 2) | (v1 >> 4);
4273 output[1] = (v1 << 4) | (v2 >> 2);
4274 Ok(2)
4275 }
4276 (b'=', _) | (_, b'=') => Err(DecodeError::InvalidPadding {
4277 index: first_padding_index(input),
4278 }),
4279 _ => {
4280 if output.len() < 3 {
4281 return Err(DecodeError::OutputTooSmall {
4282 required: 3,
4283 available: output.len(),
4284 });
4285 }
4286 let v2 = decode_byte::<A>(b2, 2)?;
4287 let v3 = decode_byte::<A>(b3, 3)?;
4288 output[0] = (v0 << 2) | (v1 >> 4);
4289 output[1] = (v1 << 4) | (v2 >> 2);
4290 output[2] = (v2 << 6) | v3;
4291 Ok(3)
4292 }
4293 }
4294}
4295
4296fn validate_tail_unpadded<A: Alphabet>(input: &[u8]) -> Result<(), DecodeError> {
4297 match input.len() {
4298 0 => Ok(()),
4299 2 => {
4300 decode_byte::<A>(input[0], 0)?;
4301 let v1 = decode_byte::<A>(input[1], 1)?;
4302 if v1 & 0b0000_1111 != 0 {
4303 return Err(DecodeError::InvalidPadding { index: 1 });
4304 }
4305 Ok(())
4306 }
4307 3 => {
4308 decode_byte::<A>(input[0], 0)?;
4309 decode_byte::<A>(input[1], 1)?;
4310 let v2 = decode_byte::<A>(input[2], 2)?;
4311 if v2 & 0b0000_0011 != 0 {
4312 return Err(DecodeError::InvalidPadding { index: 2 });
4313 }
4314 Ok(())
4315 }
4316 _ => Err(DecodeError::InvalidLength),
4317 }
4318}
4319
4320fn decode_tail_unpadded<A: Alphabet>(
4321 input: &[u8],
4322 output: &mut [u8],
4323) -> Result<usize, DecodeError> {
4324 match input.len() {
4325 0 => Ok(0),
4326 2 => {
4327 if output.is_empty() {
4328 return Err(DecodeError::OutputTooSmall {
4329 required: 1,
4330 available: output.len(),
4331 });
4332 }
4333 let v0 = decode_byte::<A>(input[0], 0)?;
4334 let v1 = decode_byte::<A>(input[1], 1)?;
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 3 => {
4342 if output.len() < 2 {
4343 return Err(DecodeError::OutputTooSmall {
4344 required: 2,
4345 available: output.len(),
4346 });
4347 }
4348 let v0 = decode_byte::<A>(input[0], 0)?;
4349 let v1 = decode_byte::<A>(input[1], 1)?;
4350 let v2 = decode_byte::<A>(input[2], 2)?;
4351 if v2 & 0b0000_0011 != 0 {
4352 return Err(DecodeError::InvalidPadding { index: 2 });
4353 }
4354 output[0] = (v0 << 2) | (v1 >> 4);
4355 output[1] = (v1 << 4) | (v2 >> 2);
4356 Ok(2)
4357 }
4358 _ => Err(DecodeError::InvalidLength),
4359 }
4360}
4361
4362fn decode_byte<A: Alphabet>(byte: u8, index: usize) -> Result<u8, DecodeError> {
4363 A::decode(byte).ok_or(DecodeError::InvalidByte { index, byte })
4364}
4365
4366fn ct_decode_slice<A: Alphabet, const PAD: bool>(
4367 input: &[u8],
4368 output: &mut [u8],
4369) -> Result<usize, DecodeError> {
4370 if input.is_empty() {
4371 return Ok(0);
4372 }
4373
4374 if PAD {
4375 ct_decode_padded::<A>(input, output)
4376 } else {
4377 ct_decode_unpadded::<A>(input, output)
4378 }
4379}
4380
4381fn ct_decode_in_place<A: Alphabet, const PAD: bool>(
4382 buffer: &mut [u8],
4383) -> Result<usize, DecodeError> {
4384 if buffer.is_empty() {
4385 return Ok(0);
4386 }
4387
4388 if PAD {
4389 ct_decode_padded_in_place::<A>(buffer)
4390 } else {
4391 ct_decode_unpadded_in_place::<A>(buffer)
4392 }
4393}
4394
4395fn ct_validate_decode<A: Alphabet, const PAD: bool>(input: &[u8]) -> Result<(), DecodeError> {
4396 if input.is_empty() {
4397 return Ok(());
4398 }
4399
4400 if PAD {
4401 ct_validate_padded::<A>(input)
4402 } else {
4403 ct_validate_unpadded::<A>(input)
4404 }
4405}
4406
4407fn ct_validate_padded<A: Alphabet>(input: &[u8]) -> Result<(), DecodeError> {
4408 if !input.len().is_multiple_of(4) {
4409 return Err(DecodeError::InvalidLength);
4410 }
4411
4412 let padding = ct_padding_len(input);
4413 let mut invalid_byte = 0u8;
4414 let mut invalid_padding = 0u8;
4415 let mut read = 0;
4416
4417 while read + 4 < input.len() {
4418 let [b0, b1, b2, b3] = read_quad(input, read)?;
4419 let (_, valid0) = ct_decode_ascii_base64::<A>(b0);
4420 let (_, valid1) = ct_decode_ascii_base64::<A>(b1);
4421 let (_, valid2) = ct_decode_ascii_base64::<A>(b2);
4422 let (_, valid3) = ct_decode_ascii_base64::<A>(b3);
4423
4424 invalid_byte |= !valid0;
4425 invalid_byte |= !valid1;
4426 invalid_byte |= !valid2;
4427 invalid_byte |= !valid3;
4428 invalid_padding |= ct_mask_eq_u8(b2, b'=');
4429 invalid_padding |= ct_mask_eq_u8(b3, b'=');
4430 read += 4;
4431 }
4432
4433 let final_chunk = read_quad(input, read)?;
4434 let (_, final_invalid_byte, final_invalid_padding, _) =
4435 ct_padded_final_quantum::<A>(final_chunk, padding);
4436 invalid_byte |= final_invalid_byte;
4437 invalid_padding |= final_invalid_padding;
4438
4439 report_ct_error(invalid_byte, invalid_padding)
4440}
4441
4442fn ct_validate_unpadded<A: Alphabet>(input: &[u8]) -> Result<(), DecodeError> {
4443 if input.len() % 4 == 1 {
4444 return Err(DecodeError::InvalidLength);
4445 }
4446
4447 let mut invalid_byte = 0u8;
4448 let mut invalid_padding = 0u8;
4449 let mut read = 0;
4450
4451 while read + 4 <= input.len() {
4452 let b0 = input[read];
4453 let b1 = input[read + 1];
4454 let b2 = input[read + 2];
4455 let b3 = input[read + 3];
4456 let (_, valid0) = ct_decode_ascii_base64::<A>(b0);
4457 let (_, valid1) = ct_decode_ascii_base64::<A>(b1);
4458 let (_, valid2) = ct_decode_ascii_base64::<A>(b2);
4459 let (_, valid3) = ct_decode_ascii_base64::<A>(b3);
4460
4461 invalid_byte |= !valid0;
4462 invalid_byte |= !valid1;
4463 invalid_byte |= !valid2;
4464 invalid_byte |= !valid3;
4465 invalid_padding |= ct_mask_eq_u8(b0, b'=');
4466 invalid_padding |= ct_mask_eq_u8(b1, b'=');
4467 invalid_padding |= ct_mask_eq_u8(b2, b'=');
4468 invalid_padding |= ct_mask_eq_u8(b3, b'=');
4469
4470 read += 4;
4471 }
4472
4473 match input.len() - read {
4474 0 => {}
4475 2 => {
4476 let b0 = input[read];
4477 let b1 = input[read + 1];
4478 let (_, valid0) = ct_decode_ascii_base64::<A>(b0);
4479 let (v1, valid1) = ct_decode_ascii_base64::<A>(b1);
4480 invalid_byte |= !valid0;
4481 invalid_byte |= !valid1;
4482 invalid_padding |= ct_mask_eq_u8(b0, b'=');
4483 invalid_padding |= ct_mask_eq_u8(b1, b'=');
4484 invalid_padding |= ct_mask_nonzero_u8(v1 & 0b0000_1111);
4485 }
4486 3 => {
4487 let b0 = input[read];
4488 let b1 = input[read + 1];
4489 let b2 = input[read + 2];
4490 let (_, valid0) = ct_decode_ascii_base64::<A>(b0);
4491 let (_, valid1) = ct_decode_ascii_base64::<A>(b1);
4492 let (v2, valid2) = ct_decode_ascii_base64::<A>(b2);
4493 invalid_byte |= !valid0;
4494 invalid_byte |= !valid1;
4495 invalid_byte |= !valid2;
4496 invalid_padding |= ct_mask_eq_u8(b0, b'=');
4497 invalid_padding |= ct_mask_eq_u8(b1, b'=');
4498 invalid_padding |= ct_mask_eq_u8(b2, b'=');
4499 invalid_padding |= ct_mask_nonzero_u8(v2 & 0b0000_0011);
4500 }
4501 _ => return Err(DecodeError::InvalidLength),
4502 }
4503
4504 report_ct_error(invalid_byte, invalid_padding)
4505}
4506
4507fn ct_padded_final_quantum<A: Alphabet>(
4508 input: [u8; 4],
4509 padding: usize,
4510) -> ([u8; 3], u8, u8, usize) {
4511 let [b0, b1, b2, b3] = input;
4512 let (v0, valid0) = ct_decode_ascii_base64::<A>(b0);
4513 let (v1, valid1) = ct_decode_ascii_base64::<A>(b1);
4514 let (v2, valid2) = ct_decode_ascii_base64::<A>(b2);
4515 let (v3, valid3) = ct_decode_ascii_base64::<A>(b3);
4516
4517 let padding_byte = padding.to_le_bytes()[0];
4518 let no_padding = ct_mask_eq_u8(padding_byte, 0);
4519 let one_padding = ct_mask_eq_u8(padding_byte, 1);
4520 let two_padding = ct_mask_eq_u8(padding_byte, 2);
4521 let require_v2 = no_padding | one_padding;
4522 let require_v3 = no_padding;
4523
4524 let invalid_byte = !valid0 | !valid1 | (!valid2 & require_v2) | (!valid3 & require_v3);
4525 let invalid_padding = (ct_mask_nonzero_u8(v1 & 0b0000_1111) & two_padding)
4526 | ((ct_mask_eq_u8(b2, b'=') | ct_mask_nonzero_u8(v2 & 0b0000_0011)) & one_padding)
4527 | ((ct_mask_eq_u8(b2, b'=') | ct_mask_eq_u8(b3, b'=')) & no_padding);
4528
4529 (
4530 [(v0 << 2) | (v1 >> 4), (v1 << 4) | (v2 >> 2), (v2 << 6) | v3],
4531 invalid_byte,
4532 invalid_padding,
4533 3 - padding,
4534 )
4535}
4536
4537fn ct_decode_padded<A: Alphabet>(input: &[u8], output: &mut [u8]) -> Result<usize, DecodeError> {
4538 if !input.len().is_multiple_of(4) {
4539 return Err(DecodeError::InvalidLength);
4540 }
4541
4542 let padding = ct_padding_len(input);
4543 let required = input.len() / 4 * 3 - padding;
4544 if output.len() < required {
4545 return Err(DecodeError::OutputTooSmall {
4546 required,
4547 available: output.len(),
4548 });
4549 }
4550
4551 let mut invalid_byte = 0u8;
4552 let mut invalid_padding = 0u8;
4553 let mut write = 0;
4554 let mut read = 0;
4555
4556 while read + 4 < input.len() {
4557 let [b0, b1, b2, b3] = read_quad(input, read)?;
4558 let (v0, valid0) = ct_decode_ascii_base64::<A>(b0);
4559 let (v1, valid1) = ct_decode_ascii_base64::<A>(b1);
4560 let (v2, valid2) = ct_decode_ascii_base64::<A>(b2);
4561 let (v3, valid3) = ct_decode_ascii_base64::<A>(b3);
4562
4563 invalid_byte |= !valid0;
4564 invalid_byte |= !valid1;
4565 invalid_byte |= !valid2;
4566 invalid_byte |= !valid3;
4567 invalid_padding |= ct_mask_eq_u8(b2, b'=');
4568 invalid_padding |= ct_mask_eq_u8(b3, b'=');
4569 output[write] = (v0 << 2) | (v1 >> 4);
4570 output[write + 1] = (v1 << 4) | (v2 >> 2);
4571 output[write + 2] = (v2 << 6) | v3;
4572 write += 3;
4573 read += 4;
4574 }
4575
4576 let final_chunk = read_quad(input, read)?;
4577 let (final_bytes, final_invalid_byte, final_invalid_padding, final_written) =
4578 ct_padded_final_quantum::<A>(final_chunk, padding);
4579 invalid_byte |= final_invalid_byte;
4580 invalid_padding |= final_invalid_padding;
4581 output[write..write + final_written].copy_from_slice(&final_bytes[..final_written]);
4582 write += final_written;
4583
4584 report_ct_error(invalid_byte, invalid_padding)?;
4585 Ok(write)
4586}
4587
4588fn ct_decode_padded_in_place<A: Alphabet>(buffer: &mut [u8]) -> Result<usize, DecodeError> {
4589 if !buffer.len().is_multiple_of(4) {
4590 return Err(DecodeError::InvalidLength);
4591 }
4592
4593 let padding = ct_padding_len(buffer);
4594 let required = buffer.len() / 4 * 3 - padding;
4595 debug_assert!(required <= buffer.len());
4596
4597 let mut invalid_byte = 0u8;
4598 let mut invalid_padding = 0u8;
4599 let mut write = 0;
4600 let mut read = 0;
4601
4602 while read + 4 < buffer.len() {
4603 let [b0, b1, b2, b3] = read_quad(buffer, read)?;
4604 let (v0, valid0) = ct_decode_ascii_base64::<A>(b0);
4605 let (v1, valid1) = ct_decode_ascii_base64::<A>(b1);
4606 let (v2, valid2) = ct_decode_ascii_base64::<A>(b2);
4607 let (v3, valid3) = ct_decode_ascii_base64::<A>(b3);
4608
4609 invalid_byte |= !valid0;
4610 invalid_byte |= !valid1;
4611 invalid_byte |= !valid2;
4612 invalid_byte |= !valid3;
4613 invalid_padding |= ct_mask_eq_u8(b2, b'=');
4614 invalid_padding |= ct_mask_eq_u8(b3, b'=');
4615 buffer[write] = (v0 << 2) | (v1 >> 4);
4616 buffer[write + 1] = (v1 << 4) | (v2 >> 2);
4617 buffer[write + 2] = (v2 << 6) | v3;
4618 write += 3;
4619 read += 4;
4620 }
4621
4622 let final_chunk = read_quad(buffer, read)?;
4623 let (final_bytes, final_invalid_byte, final_invalid_padding, final_written) =
4624 ct_padded_final_quantum::<A>(final_chunk, padding);
4625 invalid_byte |= final_invalid_byte;
4626 invalid_padding |= final_invalid_padding;
4627 buffer[write..write + final_written].copy_from_slice(&final_bytes[..final_written]);
4628 write += final_written;
4629
4630 debug_assert_eq!(write, required);
4631 report_ct_error(invalid_byte, invalid_padding)?;
4632 Ok(write)
4633}
4634
4635fn ct_decode_unpadded<A: Alphabet>(input: &[u8], output: &mut [u8]) -> Result<usize, DecodeError> {
4636 if input.len() % 4 == 1 {
4637 return Err(DecodeError::InvalidLength);
4638 }
4639
4640 let required = decoded_capacity(input.len());
4641 if output.len() < required {
4642 return Err(DecodeError::OutputTooSmall {
4643 required,
4644 available: output.len(),
4645 });
4646 }
4647
4648 let mut invalid_byte = 0u8;
4649 let mut invalid_padding = 0u8;
4650 let mut write = 0;
4651 let mut read = 0;
4652
4653 while read + 4 <= input.len() {
4654 let b0 = input[read];
4655 let b1 = input[read + 1];
4656 let b2 = input[read + 2];
4657 let b3 = input[read + 3];
4658 let (v0, valid0) = ct_decode_ascii_base64::<A>(b0);
4659 let (v1, valid1) = ct_decode_ascii_base64::<A>(b1);
4660 let (v2, valid2) = ct_decode_ascii_base64::<A>(b2);
4661 let (v3, valid3) = ct_decode_ascii_base64::<A>(b3);
4662
4663 invalid_byte |= !valid0;
4664 invalid_byte |= !valid1;
4665 invalid_byte |= !valid2;
4666 invalid_byte |= !valid3;
4667 invalid_padding |= ct_mask_eq_u8(b0, b'=');
4668 invalid_padding |= ct_mask_eq_u8(b1, b'=');
4669 invalid_padding |= ct_mask_eq_u8(b2, b'=');
4670 invalid_padding |= ct_mask_eq_u8(b3, b'=');
4671
4672 output[write] = (v0 << 2) | (v1 >> 4);
4673 output[write + 1] = (v1 << 4) | (v2 >> 2);
4674 output[write + 2] = (v2 << 6) | v3;
4675 read += 4;
4676 write += 3;
4677 }
4678
4679 match input.len() - read {
4680 0 => {}
4681 2 => {
4682 let b0 = input[read];
4683 let b1 = input[read + 1];
4684 let (v0, valid0) = ct_decode_ascii_base64::<A>(b0);
4685 let (v1, valid1) = ct_decode_ascii_base64::<A>(b1);
4686 invalid_byte |= !valid0;
4687 invalid_byte |= !valid1;
4688 invalid_padding |= ct_mask_eq_u8(b0, b'=');
4689 invalid_padding |= ct_mask_eq_u8(b1, b'=');
4690 invalid_padding |= ct_mask_nonzero_u8(v1 & 0b0000_1111);
4691 output[write] = (v0 << 2) | (v1 >> 4);
4692 write += 1;
4693 }
4694 3 => {
4695 let b0 = input[read];
4696 let b1 = input[read + 1];
4697 let b2 = input[read + 2];
4698 let (v0, valid0) = ct_decode_ascii_base64::<A>(b0);
4699 let (v1, valid1) = ct_decode_ascii_base64::<A>(b1);
4700 let (v2, valid2) = ct_decode_ascii_base64::<A>(b2);
4701 invalid_byte |= !valid0;
4702 invalid_byte |= !valid1;
4703 invalid_byte |= !valid2;
4704 invalid_padding |= ct_mask_eq_u8(b0, b'=');
4705 invalid_padding |= ct_mask_eq_u8(b1, b'=');
4706 invalid_padding |= ct_mask_eq_u8(b2, b'=');
4707 invalid_padding |= ct_mask_nonzero_u8(v2 & 0b0000_0011);
4708 output[write] = (v0 << 2) | (v1 >> 4);
4709 output[write + 1] = (v1 << 4) | (v2 >> 2);
4710 write += 2;
4711 }
4712 _ => return Err(DecodeError::InvalidLength),
4713 }
4714
4715 report_ct_error(invalid_byte, invalid_padding)?;
4716 Ok(write)
4717}
4718
4719fn ct_decode_unpadded_in_place<A: Alphabet>(buffer: &mut [u8]) -> Result<usize, DecodeError> {
4720 if buffer.len() % 4 == 1 {
4721 return Err(DecodeError::InvalidLength);
4722 }
4723
4724 let required = decoded_capacity(buffer.len());
4725 debug_assert!(required <= buffer.len());
4726
4727 let mut invalid_byte = 0u8;
4728 let mut invalid_padding = 0u8;
4729 let mut write = 0;
4730 let mut read = 0;
4731
4732 while read + 4 <= buffer.len() {
4733 let b0 = buffer[read];
4734 let b1 = buffer[read + 1];
4735 let b2 = buffer[read + 2];
4736 let b3 = buffer[read + 3];
4737 let (v0, valid0) = ct_decode_ascii_base64::<A>(b0);
4738 let (v1, valid1) = ct_decode_ascii_base64::<A>(b1);
4739 let (v2, valid2) = ct_decode_ascii_base64::<A>(b2);
4740 let (v3, valid3) = ct_decode_ascii_base64::<A>(b3);
4741
4742 invalid_byte |= !valid0;
4743 invalid_byte |= !valid1;
4744 invalid_byte |= !valid2;
4745 invalid_byte |= !valid3;
4746 invalid_padding |= ct_mask_eq_u8(b0, b'=');
4747 invalid_padding |= ct_mask_eq_u8(b1, b'=');
4748 invalid_padding |= ct_mask_eq_u8(b2, b'=');
4749 invalid_padding |= ct_mask_eq_u8(b3, b'=');
4750
4751 buffer[write] = (v0 << 2) | (v1 >> 4);
4752 buffer[write + 1] = (v1 << 4) | (v2 >> 2);
4753 buffer[write + 2] = (v2 << 6) | v3;
4754 read += 4;
4755 write += 3;
4756 }
4757
4758 match buffer.len() - read {
4759 0 => {}
4760 2 => {
4761 let b0 = buffer[read];
4762 let b1 = buffer[read + 1];
4763 let (v0, valid0) = ct_decode_ascii_base64::<A>(b0);
4764 let (v1, valid1) = ct_decode_ascii_base64::<A>(b1);
4765 invalid_byte |= !valid0;
4766 invalid_byte |= !valid1;
4767 invalid_padding |= ct_mask_eq_u8(b0, b'=');
4768 invalid_padding |= ct_mask_eq_u8(b1, b'=');
4769 invalid_padding |= ct_mask_nonzero_u8(v1 & 0b0000_1111);
4770 buffer[write] = (v0 << 2) | (v1 >> 4);
4771 write += 1;
4772 }
4773 3 => {
4774 let b0 = buffer[read];
4775 let b1 = buffer[read + 1];
4776 let b2 = buffer[read + 2];
4777 let (v0, valid0) = ct_decode_ascii_base64::<A>(b0);
4778 let (v1, valid1) = ct_decode_ascii_base64::<A>(b1);
4779 let (v2, valid2) = ct_decode_ascii_base64::<A>(b2);
4780 invalid_byte |= !valid0;
4781 invalid_byte |= !valid1;
4782 invalid_byte |= !valid2;
4783 invalid_padding |= ct_mask_eq_u8(b0, b'=');
4784 invalid_padding |= ct_mask_eq_u8(b1, b'=');
4785 invalid_padding |= ct_mask_eq_u8(b2, b'=');
4786 invalid_padding |= ct_mask_nonzero_u8(v2 & 0b0000_0011);
4787 buffer[write] = (v0 << 2) | (v1 >> 4);
4788 buffer[write + 1] = (v1 << 4) | (v2 >> 2);
4789 write += 2;
4790 }
4791 _ => return Err(DecodeError::InvalidLength),
4792 }
4793
4794 debug_assert_eq!(write, required);
4795 report_ct_error(invalid_byte, invalid_padding)?;
4796 Ok(write)
4797}
4798
4799#[inline]
4800fn ct_decode_ascii_base64<A: Alphabet>(byte: u8) -> (u8, u8) {
4801 let upper = ct_mask_lt_u8(byte.wrapping_sub(b'A'), 26);
4802 let lower = ct_mask_lt_u8(byte.wrapping_sub(b'a'), 26);
4803 let digit = ct_mask_lt_u8(byte.wrapping_sub(b'0'), 10);
4804 let value_62 = ct_mask_eq_u8(byte, A::ENCODE[62]);
4805 let value_63 = ct_mask_eq_u8(byte, A::ENCODE[63]);
4806 let valid = upper | lower | digit | value_62 | value_63;
4807
4808 let decoded = (byte.wrapping_sub(b'A') & upper)
4809 | (byte.wrapping_sub(b'a').wrapping_add(26) & lower)
4810 | (byte.wrapping_sub(b'0').wrapping_add(52) & digit)
4811 | (0x3e & value_62)
4812 | (0x3f & value_63);
4813
4814 (decoded, valid)
4815}
4816
4817fn ct_padding_len(input: &[u8]) -> usize {
4818 let last = input[input.len() - 1];
4819 let before_last = input[input.len() - 2];
4820 usize::from(ct_mask_eq_u8(last, b'=') & 1) + usize::from(ct_mask_eq_u8(before_last, b'=') & 1)
4821}
4822
4823fn report_ct_error(invalid_byte: u8, invalid_padding: u8) -> Result<(), DecodeError> {
4824 if (invalid_byte | invalid_padding) != 0 {
4825 Err(DecodeError::InvalidInput)
4826 } else {
4827 Ok(())
4828 }
4829}
4830
4831#[cfg(kani)]
4832mod kani_proofs {
4833 use super::{STANDARD, checked_encoded_len, decoded_capacity};
4834
4835 #[kani::proof]
4836 fn checked_encoded_len_is_bounded_for_small_inputs() {
4837 let len = usize::from(kani::any::<u8>());
4838 let padded = kani::any::<bool>();
4839 let encoded = checked_encoded_len(len, padded).expect("u8 input length cannot overflow");
4840
4841 assert!(encoded >= len);
4842 assert!(encoded <= len / 3 * 4 + 4);
4843 }
4844
4845 #[kani::proof]
4846 fn decoded_capacity_is_bounded_for_small_inputs() {
4847 let len = usize::from(kani::any::<u8>());
4848 let capacity = decoded_capacity(len);
4849
4850 assert!(capacity <= len / 4 * 3 + 2);
4851 }
4852
4853 #[kani::proof]
4854 #[kani::unwind(3)]
4855 fn standard_in_place_decode_returns_prefix_within_buffer() {
4856 let mut buffer = kani::any::<[u8; 8]>();
4857 let result = STANDARD.decode_in_place(&mut buffer);
4858
4859 if let Ok(decoded) = result {
4860 assert!(decoded.len() <= 8);
4861 }
4862 }
4863
4864 #[kani::proof]
4865 #[kani::unwind(3)]
4866 fn standard_decode_slice_returns_written_within_output() {
4867 let input = kani::any::<[u8; 4]>();
4868 let mut output = kani::any::<[u8; 3]>();
4869 let result = STANDARD.decode_slice(&input, &mut output);
4870
4871 if let Ok(written) = result {
4872 assert!(written <= output.len());
4873 }
4874 }
4875
4876 #[kani::proof]
4877 #[kani::unwind(3)]
4878 fn standard_decode_slice_clear_tail_clears_output_on_error() {
4879 let input = kani::any::<[u8; 4]>();
4880 let mut output = kani::any::<[u8; 3]>();
4881 let result = STANDARD.decode_slice_clear_tail(&input, &mut output);
4882
4883 if result.is_err() {
4884 assert!(output.iter().all(|byte| *byte == 0));
4885 }
4886 }
4887
4888 #[kani::proof]
4889 #[kani::unwind(3)]
4890 fn standard_encode_slice_returns_written_within_output() {
4891 let input = kani::any::<[u8; 3]>();
4892 let mut output = kani::any::<[u8; 4]>();
4893 let result = STANDARD.encode_slice(&input, &mut output);
4894
4895 if let Ok(written) = result {
4896 assert!(written <= output.len());
4897 }
4898 }
4899
4900 #[kani::proof]
4901 #[kani::unwind(4)]
4902 fn standard_encode_in_place_returns_prefix_within_buffer() {
4903 let mut buffer = kani::any::<[u8; 8]>();
4904 let input_len = usize::from(kani::any::<u8>() % 9);
4905 let result = STANDARD.encode_in_place(&mut buffer, input_len);
4906
4907 if let Ok(encoded) = result {
4908 assert!(encoded.len() <= 8);
4909 }
4910 }
4911
4912 #[kani::proof]
4913 #[kani::unwind(3)]
4914 fn standard_clear_tail_decode_clears_buffer_on_error() {
4915 let mut buffer = kani::any::<[u8; 4]>();
4916 let result = STANDARD.decode_in_place_clear_tail(&mut buffer);
4917
4918 if result.is_err() {
4919 assert!(buffer.iter().all(|byte| *byte == 0));
4920 }
4921 }
4922}
4923
4924#[cfg(test)]
4925mod tests {
4926 use super::*;
4927
4928 fn fill_pattern(output: &mut [u8], seed: usize) {
4929 for (index, byte) in output.iter_mut().enumerate() {
4930 let value = (index * 73 + seed * 19) % 256;
4931 *byte = u8::try_from(value).unwrap();
4932 }
4933 }
4934
4935 fn assert_encode_backend_matches_scalar<A, const PAD: bool>(input: &[u8])
4936 where
4937 A: Alphabet,
4938 {
4939 let engine = Engine::<A, PAD>::new();
4940 let mut dispatched = [0x55; 256];
4941 let mut scalar = [0xaa; 256];
4942
4943 let dispatched_result = engine.encode_slice(input, &mut dispatched);
4944 let scalar_result = backend::scalar_reference_encode_slice::<A, PAD>(input, &mut scalar);
4945
4946 assert_eq!(dispatched_result, scalar_result);
4947 if let Ok(written) = dispatched_result {
4948 assert_eq!(&dispatched[..written], &scalar[..written]);
4949 }
4950
4951 let required = checked_encoded_len(input.len(), PAD).unwrap();
4952 if required > 0 {
4953 let mut dispatched_short = [0x55; 256];
4954 let mut scalar_short = [0xaa; 256];
4955 let available = required - 1;
4956
4957 assert_eq!(
4958 engine.encode_slice(input, &mut dispatched_short[..available]),
4959 backend::scalar_reference_encode_slice::<A, PAD>(
4960 input,
4961 &mut scalar_short[..available],
4962 )
4963 );
4964 }
4965 }
4966
4967 fn assert_decode_backend_matches_scalar<A, const PAD: bool>(input: &[u8])
4968 where
4969 A: Alphabet,
4970 {
4971 let engine = Engine::<A, PAD>::new();
4972 let mut dispatched = [0x55; 128];
4973 let mut scalar = [0xaa; 128];
4974
4975 let dispatched_result = engine.decode_slice(input, &mut dispatched);
4976 let scalar_result = backend::scalar_reference_decode_slice::<A, PAD>(input, &mut scalar);
4977
4978 assert_eq!(dispatched_result, scalar_result);
4979 if let Ok(written) = dispatched_result {
4980 assert_eq!(&dispatched[..written], &scalar[..written]);
4981
4982 if written > 0 {
4983 let mut dispatched_short = [0x55; 128];
4984 let mut scalar_short = [0xaa; 128];
4985 let available = written - 1;
4986
4987 assert_eq!(
4988 engine.decode_slice(input, &mut dispatched_short[..available]),
4989 backend::scalar_reference_decode_slice::<A, PAD>(
4990 input,
4991 &mut scalar_short[..available],
4992 )
4993 );
4994 }
4995 }
4996 }
4997
4998 fn assert_backend_round_trip_matches_scalar<A, const PAD: bool>(input: &[u8])
4999 where
5000 A: Alphabet,
5001 {
5002 assert_encode_backend_matches_scalar::<A, PAD>(input);
5003
5004 let mut encoded = [0; 256];
5005 let encoded_len =
5006 backend::scalar_reference_encode_slice::<A, PAD>(input, &mut encoded).unwrap();
5007 assert_decode_backend_matches_scalar::<A, PAD>(&encoded[..encoded_len]);
5008 }
5009
5010 #[test]
5011 fn backend_dispatch_matches_scalar_reference_for_canonical_inputs() {
5012 let mut input = [0; 128];
5013
5014 for input_len in 0..=input.len() {
5015 fill_pattern(&mut input[..input_len], input_len);
5016 let input = &input[..input_len];
5017
5018 assert_backend_round_trip_matches_scalar::<Standard, true>(input);
5019 assert_backend_round_trip_matches_scalar::<Standard, false>(input);
5020 assert_backend_round_trip_matches_scalar::<UrlSafe, true>(input);
5021 assert_backend_round_trip_matches_scalar::<UrlSafe, false>(input);
5022 }
5023 }
5024
5025 #[test]
5026 fn backend_dispatch_matches_scalar_reference_for_malformed_inputs() {
5027 for input in [
5028 &b"Z"[..],
5029 b"====",
5030 b"AA=A",
5031 b"Zh==",
5032 b"Zm9=",
5033 b"Zm9v$g==",
5034 b"Zm9vZh==",
5035 ] {
5036 assert_decode_backend_matches_scalar::<Standard, true>(input);
5037 }
5038
5039 for input in [&b"Z"[..], b"AA=A", b"Zh", b"Zm9", b"Zm9vYg$"] {
5040 assert_decode_backend_matches_scalar::<Standard, false>(input);
5041 }
5042
5043 assert_decode_backend_matches_scalar::<UrlSafe, true>(b"AA+A");
5044 assert_decode_backend_matches_scalar::<UrlSafe, false>(b"AA/A");
5045 assert_decode_backend_matches_scalar::<Standard, true>(b"AA-A");
5046 assert_decode_backend_matches_scalar::<Standard, false>(b"AA_A");
5047 }
5048
5049 #[cfg(feature = "simd")]
5050 #[test]
5051 fn simd_dispatch_scaffold_keeps_scalar_active() {
5052 assert_eq!(simd::active_backend(), simd::ActiveBackend::Scalar);
5053 let _candidate = simd::detected_candidate();
5054 }
5055
5056 #[test]
5057 fn encodes_standard_vectors() {
5058 let vectors = [
5059 (&b""[..], &b""[..]),
5060 (&b"f"[..], &b"Zg=="[..]),
5061 (&b"fo"[..], &b"Zm8="[..]),
5062 (&b"foo"[..], &b"Zm9v"[..]),
5063 (&b"foob"[..], &b"Zm9vYg=="[..]),
5064 (&b"fooba"[..], &b"Zm9vYmE="[..]),
5065 (&b"foobar"[..], &b"Zm9vYmFy"[..]),
5066 ];
5067 for (input, expected) in vectors {
5068 let mut output = [0u8; 16];
5069 let written = STANDARD.encode_slice(input, &mut output).unwrap();
5070 assert_eq!(&output[..written], expected);
5071 }
5072 }
5073
5074 #[test]
5075 fn decodes_standard_vectors() {
5076 let vectors = [
5077 (&b""[..], &b""[..]),
5078 (&b"Zg=="[..], &b"f"[..]),
5079 (&b"Zm8="[..], &b"fo"[..]),
5080 (&b"Zm9v"[..], &b"foo"[..]),
5081 (&b"Zm9vYg=="[..], &b"foob"[..]),
5082 (&b"Zm9vYmE="[..], &b"fooba"[..]),
5083 (&b"Zm9vYmFy"[..], &b"foobar"[..]),
5084 ];
5085 for (input, expected) in vectors {
5086 let mut output = [0u8; 16];
5087 let written = STANDARD.decode_slice(input, &mut output).unwrap();
5088 assert_eq!(&output[..written], expected);
5089 }
5090 }
5091
5092 #[test]
5093 fn supports_unpadded_url_safe() {
5094 let mut encoded = [0u8; 16];
5095 let written = URL_SAFE_NO_PAD
5096 .encode_slice(b"\xfb\xff", &mut encoded)
5097 .unwrap();
5098 assert_eq!(&encoded[..written], b"-_8");
5099
5100 let mut decoded = [0u8; 2];
5101 let written = URL_SAFE_NO_PAD
5102 .decode_slice(&encoded[..written], &mut decoded)
5103 .unwrap();
5104 assert_eq!(&decoded[..written], b"\xfb\xff");
5105 }
5106
5107 #[test]
5108 fn decodes_in_place() {
5109 let mut buffer = *b"Zm9vYmFy";
5110 let decoded = STANDARD_NO_PAD.decode_in_place(&mut buffer).unwrap();
5111 assert_eq!(decoded, b"foobar");
5112 }
5113
5114 #[test]
5115 fn rejects_non_canonical_padding_bits() {
5116 let mut output = [0u8; 4];
5117 assert_eq!(
5118 STANDARD.decode_slice(b"Zh==", &mut output),
5119 Err(DecodeError::InvalidPadding { index: 1 })
5120 );
5121 assert_eq!(
5122 STANDARD.decode_slice(b"Zm9=", &mut output),
5123 Err(DecodeError::InvalidPadding { index: 2 })
5124 );
5125 }
5126}