1#![cfg_attr(not(feature = "std"), no_std)]
2#![deny(unsafe_code)]
3#![deny(missing_docs)]
4#![deny(clippy::all)]
5#![deny(clippy::pedantic)]
6#![allow(clippy::missing_errors_doc)]
7
8#[cfg(feature = "alloc")]
47extern crate alloc;
48
49#[cfg(feature = "simd")]
50mod simd;
51
52pub mod runtime {
58 #[derive(Clone, Copy, Debug, Eq, PartialEq)]
60 #[non_exhaustive]
61 pub enum Backend {
62 Scalar,
64 Avx512Vbmi,
66 Avx2,
68 Ssse3Sse41,
70 Neon,
72 WasmSimd128,
74 }
75
76 impl Backend {
77 #[must_use]
83 pub const fn as_str(self) -> &'static str {
84 match self {
85 Self::Scalar => "scalar",
86 Self::Avx512Vbmi => "avx512-vbmi",
87 Self::Avx2 => "avx2",
88 Self::Ssse3Sse41 => "ssse3-sse4.1",
89 Self::Neon => "neon",
90 Self::WasmSimd128 => "wasm-simd128",
91 }
92 }
93
94 #[must_use]
107 pub const fn required_cpu_features(self) -> &'static [&'static str] {
108 match self {
109 Self::Scalar => &[],
110 Self::Avx512Vbmi => &["avx512f", "avx512bw", "avx512vl", "avx512vbmi"],
111 Self::Avx2 => &["avx2"],
112 Self::Ssse3Sse41 => &["ssse3", "sse4.1"],
113 Self::Neon => &["neon"],
114 Self::WasmSimd128 => &["simd128"],
115 }
116 }
117 }
118
119 impl core::fmt::Display for Backend {
120 fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
121 formatter.write_str(self.as_str())
122 }
123 }
124
125 #[derive(Clone, Copy, Debug, Eq, PartialEq)]
127 #[non_exhaustive]
128 pub enum SecurityPosture {
129 ScalarOnly,
131 SimdCandidateScalarActive,
133 Accelerated,
135 }
136
137 impl SecurityPosture {
138 #[must_use]
147 pub const fn as_str(self) -> &'static str {
148 match self {
149 Self::ScalarOnly => "scalar-only",
150 Self::SimdCandidateScalarActive => "simd-candidate-scalar-active",
151 Self::Accelerated => "accelerated",
152 }
153 }
154 }
155
156 impl core::fmt::Display for SecurityPosture {
157 fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
158 formatter.write_str(self.as_str())
159 }
160 }
161
162 #[derive(Clone, Copy, Debug, Eq, PartialEq)]
164 #[non_exhaustive]
165 pub enum BackendPolicy {
166 ScalarExecutionOnly,
168 SimdFeatureDisabled,
170 NoDetectedSimdCandidate,
172 HighAssuranceScalarOnly,
175 }
176
177 impl BackendPolicy {
178 #[must_use]
187 pub const fn as_str(self) -> &'static str {
188 match self {
189 Self::ScalarExecutionOnly => "scalar-execution-only",
190 Self::SimdFeatureDisabled => "simd-feature-disabled",
191 Self::NoDetectedSimdCandidate => "no-detected-simd-candidate",
192 Self::HighAssuranceScalarOnly => "high-assurance-scalar-only",
193 }
194 }
195 }
196
197 impl core::fmt::Display for BackendPolicy {
198 fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
199 formatter.write_str(self.as_str())
200 }
201 }
202
203 #[derive(Clone, Copy, Debug, Eq, PartialEq)]
205 pub struct BackendPolicyError {
206 pub policy: BackendPolicy,
208 pub report: BackendReport,
210 }
211
212 impl core::fmt::Display for BackendPolicyError {
213 fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
214 write!(
215 formatter,
216 "runtime backend policy `{}` was not satisfied ({})",
217 self.policy, self.report,
218 )
219 }
220 }
221
222 #[cfg(feature = "std")]
223 impl std::error::Error for BackendPolicyError {}
224
225 #[derive(Clone, Copy, Debug, Eq, PartialEq)]
227 pub struct BackendReport {
228 pub active: Backend,
230 pub candidate: Backend,
232 pub simd_feature_enabled: bool,
234 pub accelerated_backend_active: bool,
236 pub unsafe_boundary_enforced: bool,
238 pub security_posture: SecurityPosture,
240 }
241
242 #[derive(Clone, Copy, Debug, Eq, PartialEq)]
244 pub struct BackendSnapshot {
245 pub active: &'static str,
247 pub candidate: &'static str,
249 pub candidate_required_cpu_features: &'static [&'static str],
251 pub simd_feature_enabled: bool,
253 pub accelerated_backend_active: bool,
255 pub unsafe_boundary_enforced: bool,
257 pub security_posture: &'static str,
259 }
260
261 impl core::fmt::Display for BackendReport {
262 fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
263 write!(
264 formatter,
265 "active={} candidate={} candidate_required_cpu_features=",
266 self.active, self.candidate,
267 )?;
268 write_feature_list(formatter, self.candidate_required_cpu_features())?;
269 write!(
270 formatter,
271 " simd_feature_enabled={} accelerated_backend_active={} unsafe_boundary_enforced={} security_posture={}",
272 self.simd_feature_enabled,
273 self.accelerated_backend_active,
274 self.unsafe_boundary_enforced,
275 self.security_posture,
276 )
277 }
278 }
279
280 impl BackendReport {
281 #[must_use]
291 pub const fn satisfies(self, policy: BackendPolicy) -> bool {
292 match policy {
293 BackendPolicy::ScalarExecutionOnly => {
294 matches!(self.active, Backend::Scalar) && !self.accelerated_backend_active
295 }
296 BackendPolicy::SimdFeatureDisabled => !self.simd_feature_enabled,
297 BackendPolicy::NoDetectedSimdCandidate => matches!(self.candidate, Backend::Scalar),
298 BackendPolicy::HighAssuranceScalarOnly => {
299 matches!(self.active, Backend::Scalar)
300 && matches!(self.candidate, Backend::Scalar)
301 && !self.simd_feature_enabled
302 && !self.accelerated_backend_active
303 && self.unsafe_boundary_enforced
304 }
305 }
306 }
307
308 #[must_use]
319 pub const fn candidate_required_cpu_features(self) -> &'static [&'static str] {
320 self.candidate.required_cpu_features()
321 }
322
323 #[must_use]
332 pub const fn snapshot(self) -> BackendSnapshot {
333 BackendSnapshot {
334 active: self.active.as_str(),
335 candidate: self.candidate.as_str(),
336 candidate_required_cpu_features: self.candidate_required_cpu_features(),
337 simd_feature_enabled: self.simd_feature_enabled,
338 accelerated_backend_active: self.accelerated_backend_active,
339 unsafe_boundary_enforced: self.unsafe_boundary_enforced,
340 security_posture: self.security_posture.as_str(),
341 }
342 }
343 }
344
345 #[must_use]
354 pub fn backend_report() -> BackendReport {
355 let active = active_backend();
356 let candidate = detected_candidate();
357 let accelerated_backend_active = active != Backend::Scalar;
358 let security_posture = if accelerated_backend_active {
359 SecurityPosture::Accelerated
360 } else if candidate != Backend::Scalar {
361 SecurityPosture::SimdCandidateScalarActive
362 } else {
363 SecurityPosture::ScalarOnly
364 };
365
366 BackendReport {
367 active,
368 candidate,
369 simd_feature_enabled: cfg!(feature = "simd"),
370 accelerated_backend_active,
371 unsafe_boundary_enforced: true,
372 security_posture,
373 }
374 }
375
376 pub fn require_backend_policy(policy: BackendPolicy) -> Result<(), BackendPolicyError> {
385 let report = backend_report();
386 if report.satisfies(policy) {
387 Ok(())
388 } else {
389 Err(BackendPolicyError { policy, report })
390 }
391 }
392
393 fn write_feature_list(
394 formatter: &mut core::fmt::Formatter<'_>,
395 features: &[&str],
396 ) -> core::fmt::Result {
397 formatter.write_str("[")?;
398 let mut index = 0;
399 while index < features.len() {
400 if index != 0 {
401 formatter.write_str(",")?;
402 }
403 formatter.write_str(features[index])?;
404 index += 1;
405 }
406 formatter.write_str("]")
407 }
408
409 #[cfg(feature = "simd")]
410 fn active_backend() -> Backend {
411 match super::simd::active_backend() {
412 super::simd::ActiveBackend::Scalar => Backend::Scalar,
413 }
414 }
415
416 #[cfg(not(feature = "simd"))]
417 const fn active_backend() -> Backend {
418 Backend::Scalar
419 }
420
421 #[cfg(feature = "simd")]
422 fn detected_candidate() -> Backend {
423 match super::simd::detected_candidate() {
424 super::simd::Candidate::Scalar => Backend::Scalar,
425 #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
426 super::simd::Candidate::Avx512Vbmi => Backend::Avx512Vbmi,
427 #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
428 super::simd::Candidate::Avx2 => Backend::Avx2,
429 #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
430 super::simd::Candidate::Ssse3Sse41 => Backend::Ssse3Sse41,
431 #[cfg(any(target_arch = "aarch64", target_arch = "arm"))]
432 super::simd::Candidate::Neon => Backend::Neon,
433 #[cfg(target_arch = "wasm32")]
434 super::simd::Candidate::WasmSimd128 => Backend::WasmSimd128,
435 }
436 }
437
438 #[cfg(not(feature = "simd"))]
439 const fn detected_candidate() -> Backend {
440 Backend::Scalar
441 }
442}
443
444#[cfg(feature = "stream")]
445pub mod stream {
446 use super::{Alphabet, DecodeError, EncodeError, Engine};
481 use std::io::{self, Read, Write};
482
483 struct OutputQueue<const CAP: usize> {
484 buffer: [u8; CAP],
485 start: usize,
486 len: usize,
487 }
488
489 impl<const CAP: usize> OutputQueue<CAP> {
490 const fn new() -> Self {
491 Self {
492 buffer: [0; CAP],
493 start: 0,
494 len: 0,
495 }
496 }
497
498 const fn is_empty(&self) -> bool {
499 self.len == 0
500 }
501
502 const fn len(&self) -> usize {
503 self.len
504 }
505
506 const fn capacity(&self) -> usize {
507 self.len + self.available_capacity()
508 }
509
510 fn push_slice(&mut self, input: &[u8]) -> io::Result<()> {
511 if input.len() > self.available_capacity() {
512 return Err(io::Error::new(
513 io::ErrorKind::InvalidInput,
514 "base64 stream output queue capacity exceeded",
515 ));
516 }
517
518 let mut read = 0;
519 while read < input.len() {
520 let write = (self.start + self.len) % CAP;
521 self.buffer[write] = input[read];
522 self.len += 1;
523 read += 1;
524 }
525
526 Ok(())
527 }
528
529 fn copy_front(&self, output: &mut [u8]) -> usize {
530 let count = core::cmp::min(self.len, output.len());
531 let first = core::cmp::min(count, CAP - self.start);
532 output[..first].copy_from_slice(&self.buffer[self.start..self.start + first]);
533
534 let second = count - first;
535 if second > 0 {
536 output[first..first + second].copy_from_slice(&self.buffer[..second]);
537 }
538
539 count
540 }
541
542 fn discard_front(&mut self, count: usize) {
543 let count = core::cmp::min(count, self.len);
544 let first = core::cmp::min(count, CAP - self.start);
545 crate::wipe_bytes(&mut self.buffer[self.start..self.start + first]);
546
547 let second = count - first;
548 if second > 0 {
549 crate::wipe_bytes(&mut self.buffer[..second]);
550 }
551
552 self.start = (self.start + count) % CAP;
553 self.len -= count;
554 if self.len == 0 {
555 self.start = 0;
556 }
557 }
558
559 fn pop_slice(&mut self, output: &mut [u8]) -> usize {
560 let count = self.copy_front(output);
561 self.discard_front(count);
562 count
563 }
564
565 fn clear_all(&mut self) {
566 crate::wipe_bytes(&mut self.buffer);
567 self.start = 0;
568 self.len = 0;
569 }
570
571 const fn available_capacity(&self) -> usize {
572 CAP - self.len
573 }
574 }
575
576 pub struct Encoder<W, A, const PAD: bool>
584 where
585 A: Alphabet,
586 {
587 inner: Option<W>,
588 engine: Engine<A, PAD>,
589 pending: [u8; 2],
590 pending_len: usize,
591 output: OutputQueue<1024>,
592 finalized: bool,
593 }
594
595 impl<W, A, const PAD: bool> Encoder<W, A, PAD>
596 where
597 A: Alphabet,
598 {
599 #[must_use]
601 pub const fn new(inner: W, engine: Engine<A, PAD>) -> Self {
602 Self {
603 inner: Some(inner),
604 engine,
605 pending: [0; 2],
606 pending_len: 0,
607 output: OutputQueue::new(),
608 finalized: false,
609 }
610 }
611
612 #[must_use]
614 pub fn get_ref(&self) -> &W {
615 self.inner_ref()
616 }
617
618 pub fn get_mut(&mut self) -> &mut W {
620 self.inner_mut()
621 }
622
623 #[must_use]
625 pub const fn engine(&self) -> Engine<A, PAD> {
626 self.engine
627 }
628
629 #[must_use]
631 pub const fn is_padded(&self) -> bool {
632 PAD
633 }
634
635 #[must_use]
638 pub const fn pending_len(&self) -> usize {
639 self.pending_len
640 }
641
642 #[must_use]
645 pub const fn has_pending_input(&self) -> bool {
646 self.pending_len != 0
647 }
648
649 #[must_use]
654 pub const fn pending_input_needed_len(&self) -> usize {
655 if self.has_pending_input() {
656 3 - self.pending_len
657 } else {
658 0
659 }
660 }
661
662 #[must_use]
665 pub const fn buffered_output_len(&self) -> usize {
666 self.output.len()
667 }
668
669 #[must_use]
672 pub const fn buffered_output_capacity(&self) -> usize {
673 self.output.capacity()
674 }
675
676 #[must_use]
679 pub const fn buffered_output_remaining_capacity(&self) -> usize {
680 self.output.available_capacity()
681 }
682
683 #[must_use]
686 pub const fn has_buffered_output(&self) -> bool {
687 !self.output.is_empty()
688 }
689
690 #[must_use]
694 pub const fn is_finalized(&self) -> bool {
695 self.finalized
696 }
697
698 #[must_use]
701 pub const fn can_into_inner(&self) -> bool {
702 !self.has_pending_input() && !self.has_buffered_output()
703 }
704
705 #[must_use]
709 pub fn into_inner(mut self) -> W {
710 self.take_inner()
711 }
712
713 #[allow(clippy::result_large_err)]
719 pub fn try_into_inner(mut self) -> Result<W, Self> {
720 if !self.can_into_inner() {
721 return Err(self);
722 }
723 Ok(self.take_inner())
724 }
725
726 fn inner_ref(&self) -> &W {
727 match &self.inner {
728 Some(inner) => inner,
729 None => unreachable!("stream encoder inner writer was already taken"),
730 }
731 }
732
733 fn inner_mut(&mut self) -> &mut W {
734 match &mut self.inner {
735 Some(inner) => inner,
736 None => unreachable!("stream encoder inner writer was already taken"),
737 }
738 }
739
740 fn take_inner(&mut self) -> W {
741 match self.inner.take() {
742 Some(inner) => inner,
743 None => unreachable!("stream encoder inner writer was already taken"),
744 }
745 }
746
747 fn clear_pending(&mut self) {
748 crate::wipe_bytes(&mut self.pending);
749 self.pending_len = 0;
750 }
751
752 fn clear_output(&mut self) {
753 self.output.clear_all();
754 }
755 }
756
757 impl<W, A, const PAD: bool> Drop for Encoder<W, A, PAD>
758 where
759 A: Alphabet,
760 {
761 fn drop(&mut self) {
762 self.clear_pending();
763 self.clear_output();
764 }
765 }
766
767 impl<W, A, const PAD: bool> core::fmt::Debug for Encoder<W, A, PAD>
768 where
769 A: Alphabet,
770 {
771 fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
772 formatter
773 .debug_struct("Encoder")
774 .field("inner", &redacted_inner_state(self.inner.is_some()))
775 .field("engine", &self.engine)
776 .field("pending", &"<redacted>")
777 .field("pending_len", &self.pending_len)
778 .field("pending_input_needed_len", &self.pending_input_needed_len())
779 .field("buffered_output_len", &self.output.len())
780 .field("buffered_output_capacity", &self.output.capacity())
781 .field(
782 "buffered_output_remaining_capacity",
783 &self.output.available_capacity(),
784 )
785 .field("can_into_inner", &self.can_into_inner())
786 .field("finalized", &self.finalized)
787 .finish()
788 }
789 }
790
791 impl<W, A, const PAD: bool> Encoder<W, A, PAD>
792 where
793 W: Write,
794 A: Alphabet,
795 {
796 pub fn try_finish(&mut self) -> io::Result<()> {
806 if !self.finalized {
807 self.queue_pending_final()?;
808 self.finalized = true;
809 }
810 self.flush()
811 }
812
813 pub fn finish(mut self) -> io::Result<W> {
815 self.try_finish()?;
816 Ok(self.take_inner())
817 }
818
819 fn queue_pending_final(&mut self) -> io::Result<()> {
820 if self.pending_len == 0 {
821 return Ok(());
822 }
823
824 let mut pending = [0u8; 2];
825 pending[..self.pending_len].copy_from_slice(&self.pending[..self.pending_len]);
826 let pending_len = self.pending_len;
827 let mut encoded = [0u8; 4];
828 let result = self.queue_encoded_temp(&pending[..pending_len], &mut encoded);
829 crate::wipe_bytes(&mut pending);
830 result?;
831 self.clear_pending();
832 Ok(())
833 }
834
835 fn queue_encoded_temp(&mut self, input: &[u8], encoded: &mut [u8]) -> io::Result<()> {
836 let written = match self.engine.encode_slice(input, encoded) {
837 Ok(written) => written,
838 Err(err) => {
839 crate::wipe_bytes(encoded);
840 return Err(encode_error_to_io(err));
841 }
842 };
843
844 let result = self.output.push_slice(&encoded[..written]);
845 crate::wipe_bytes(encoded);
846 result
847 }
848
849 fn drain_output(&mut self) -> io::Result<()> {
850 let mut chunk = [0u8; 1024];
851 while !self.output.is_empty() {
852 let pending = self.output.copy_front(&mut chunk);
853 let result = self.inner_mut().write(&chunk[..pending]);
854 crate::wipe_bytes(&mut chunk[..pending]);
855 match result {
856 Ok(0) => {
857 return Err(io::Error::new(
858 io::ErrorKind::WriteZero,
859 "base64 stream encoder could not drain buffered output",
860 ));
861 }
862 Ok(written) => {
863 if written > pending {
864 return Err(io::Error::new(
865 io::ErrorKind::InvalidData,
866 "wrapped writer reported more bytes than provided",
867 ));
868 }
869 self.output.discard_front(written);
870 }
871 Err(err) => return Err(err),
872 }
873 }
874
875 Ok(())
876 }
877 }
878
879 impl<W, A, const PAD: bool> Write for Encoder<W, A, PAD>
880 where
881 W: Write,
882 A: Alphabet,
883 {
884 fn write(&mut self, input: &[u8]) -> io::Result<usize> {
885 if input.is_empty() {
886 self.drain_output()?;
887 return Ok(0);
888 }
889 self.drain_output()?;
890 if self.finalized {
891 return Err(io::Error::new(
892 io::ErrorKind::InvalidInput,
893 "base64 stream encoder received input after finalization",
894 ));
895 }
896
897 let mut consumed = 0;
898 if self.pending_len > 0 {
899 let needed = 3 - self.pending_len;
900 if input.len() < needed {
901 self.pending[self.pending_len..self.pending_len + input.len()]
902 .copy_from_slice(input);
903 self.pending_len += input.len();
904 return Ok(input.len());
905 }
906
907 let mut chunk = [0u8; 3];
908 chunk[..self.pending_len].copy_from_slice(&self.pending[..self.pending_len]);
909 chunk[self.pending_len..].copy_from_slice(&input[..needed]);
910
911 let mut encoded = [0u8; 4];
912 let result = self.queue_encoded_temp(&chunk, &mut encoded);
913 crate::wipe_bytes(&mut chunk);
914 result?;
915 self.clear_pending();
916 consumed += needed;
917 return Ok(consumed);
918 }
919
920 let remaining = &input[consumed..];
921 let full_len = remaining.len() / 3 * 3;
922 if full_len > 0 {
923 let mut take = core::cmp::min(full_len, 768);
924 take -= take % 3;
925 debug_assert!(take > 0);
926
927 let mut encoded = [0u8; 1024];
928 self.queue_encoded_temp(&remaining[..take], &mut encoded)?;
929 return Ok(consumed + take);
930 }
931
932 let tail = &remaining[full_len..];
933 self.pending[..tail.len()].copy_from_slice(tail);
934 self.pending_len = tail.len();
935
936 Ok(input.len())
937 }
938
939 fn flush(&mut self) -> io::Result<()> {
940 self.drain_output()?;
941 self.inner_mut().flush()
942 }
943 }
944
945 fn encode_error_to_io(err: EncodeError) -> io::Error {
946 io::Error::new(io::ErrorKind::InvalidInput, err)
947 }
948
949 pub struct Decoder<W, A, const PAD: bool>
957 where
958 A: Alphabet,
959 {
960 inner: Option<W>,
961 engine: Engine<A, PAD>,
962 pending: [u8; 4],
963 pending_len: usize,
964 output: OutputQueue<1024>,
965 finished: bool,
966 finalized: bool,
967 failed: bool,
968 }
969
970 impl<W, A, const PAD: bool> Decoder<W, A, PAD>
971 where
972 A: Alphabet,
973 {
974 #[must_use]
976 pub const fn new(inner: W, engine: Engine<A, PAD>) -> Self {
977 Self {
978 inner: Some(inner),
979 engine,
980 pending: [0; 4],
981 pending_len: 0,
982 output: OutputQueue::new(),
983 finished: false,
984 finalized: false,
985 failed: false,
986 }
987 }
988
989 #[must_use]
991 pub fn get_ref(&self) -> &W {
992 self.inner_ref()
993 }
994
995 pub fn get_mut(&mut self) -> &mut W {
997 self.inner_mut()
998 }
999
1000 #[must_use]
1002 pub const fn engine(&self) -> Engine<A, PAD> {
1003 self.engine
1004 }
1005
1006 #[must_use]
1008 pub const fn is_padded(&self) -> bool {
1009 PAD
1010 }
1011
1012 #[must_use]
1015 pub const fn pending_len(&self) -> usize {
1016 self.pending_len
1017 }
1018
1019 #[must_use]
1022 pub const fn has_pending_input(&self) -> bool {
1023 self.pending_len != 0
1024 }
1025
1026 #[must_use]
1031 pub const fn pending_input_needed_len(&self) -> usize {
1032 if self.has_pending_input() {
1033 4 - self.pending_len
1034 } else {
1035 0
1036 }
1037 }
1038
1039 #[must_use]
1042 pub const fn buffered_output_len(&self) -> usize {
1043 self.output.len()
1044 }
1045
1046 #[must_use]
1049 pub const fn buffered_output_capacity(&self) -> usize {
1050 self.output.capacity()
1051 }
1052
1053 #[must_use]
1056 pub const fn buffered_output_remaining_capacity(&self) -> usize {
1057 self.output.available_capacity()
1058 }
1059
1060 #[must_use]
1063 pub const fn has_buffered_output(&self) -> bool {
1064 !self.output.is_empty()
1065 }
1066
1067 #[must_use]
1073 pub const fn has_terminal_padding(&self) -> bool {
1074 self.finished
1075 }
1076
1077 #[must_use]
1081 pub const fn is_finalized(&self) -> bool {
1082 self.finalized
1083 }
1084
1085 #[must_use]
1091 pub const fn is_failed(&self) -> bool {
1092 self.failed
1093 }
1094
1095 #[must_use]
1098 pub const fn can_into_inner(&self) -> bool {
1099 !self.is_failed() && !self.has_pending_input() && !self.has_buffered_output()
1100 }
1101
1102 #[must_use]
1106 pub fn into_inner(mut self) -> W {
1107 self.take_inner()
1108 }
1109
1110 #[allow(clippy::result_large_err)]
1116 pub fn try_into_inner(mut self) -> Result<W, Self> {
1117 if !self.can_into_inner() {
1118 return Err(self);
1119 }
1120 Ok(self.take_inner())
1121 }
1122
1123 fn inner_ref(&self) -> &W {
1124 match &self.inner {
1125 Some(inner) => inner,
1126 None => unreachable!("stream decoder inner writer was already taken"),
1127 }
1128 }
1129
1130 fn inner_mut(&mut self) -> &mut W {
1131 match &mut self.inner {
1132 Some(inner) => inner,
1133 None => unreachable!("stream decoder inner writer was already taken"),
1134 }
1135 }
1136
1137 fn take_inner(&mut self) -> W {
1138 match self.inner.take() {
1139 Some(inner) => inner,
1140 None => unreachable!("stream decoder inner writer was already taken"),
1141 }
1142 }
1143
1144 fn clear_pending(&mut self) {
1145 crate::wipe_bytes(&mut self.pending);
1146 self.pending_len = 0;
1147 }
1148
1149 fn clear_output(&mut self) {
1150 self.output.clear_all();
1151 }
1152 }
1153
1154 impl<W, A, const PAD: bool> Drop for Decoder<W, A, PAD>
1155 where
1156 A: Alphabet,
1157 {
1158 fn drop(&mut self) {
1159 self.clear_pending();
1160 self.clear_output();
1161 }
1162 }
1163
1164 impl<W, A, const PAD: bool> core::fmt::Debug for Decoder<W, A, PAD>
1165 where
1166 A: Alphabet,
1167 {
1168 fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
1169 formatter
1170 .debug_struct("Decoder")
1171 .field("inner", &redacted_inner_state(self.inner.is_some()))
1172 .field("engine", &self.engine)
1173 .field("pending", &"<redacted>")
1174 .field("pending_len", &self.pending_len)
1175 .field("pending_input_needed_len", &self.pending_input_needed_len())
1176 .field("buffered_output_len", &self.output.len())
1177 .field("buffered_output_capacity", &self.output.capacity())
1178 .field(
1179 "buffered_output_remaining_capacity",
1180 &self.output.available_capacity(),
1181 )
1182 .field("can_into_inner", &self.can_into_inner())
1183 .field("terminal_padding", &self.finished)
1184 .field("finalized", &self.finalized)
1185 .field("failed", &self.failed)
1186 .finish()
1187 }
1188 }
1189
1190 impl<W, A, const PAD: bool> Decoder<W, A, PAD>
1191 where
1192 W: Write,
1193 A: Alphabet,
1194 {
1195 pub fn try_finish(&mut self) -> io::Result<()> {
1205 if self.failed {
1206 return Err(stream_decoder_failed_error());
1207 }
1208 if !self.finalized {
1209 self.queue_pending_final()?;
1210 self.finalized = true;
1211 }
1212 self.flush()
1213 }
1214
1215 pub fn finish(mut self) -> io::Result<W> {
1217 self.try_finish()?;
1218 Ok(self.take_inner())
1219 }
1220
1221 fn queue_pending_final(&mut self) -> io::Result<()> {
1222 if self.pending_len == 0 {
1223 return Ok(());
1224 }
1225
1226 let mut pending = [0u8; 4];
1227 pending[..self.pending_len].copy_from_slice(&self.pending[..self.pending_len]);
1228 let pending_len = self.pending_len;
1229 let mut decoded = [0u8; 3];
1230 let result = self.queue_decoded_temp(&pending[..pending_len], &mut decoded);
1231 crate::wipe_bytes(&mut pending);
1232 if let Err(err) = result {
1233 self.clear_pending();
1234 return Err(err);
1235 }
1236 self.clear_pending();
1237 Ok(())
1238 }
1239
1240 fn queue_full_quad(&mut self, mut input: [u8; 4]) -> io::Result<()> {
1241 let mut decoded = [0u8; 3];
1242 let result = self.queue_decoded_temp(&input, &mut decoded);
1243 crate::wipe_bytes(&mut input);
1244 let written = result?;
1245 if written < 3 {
1246 self.finished = true;
1247 }
1248 Ok(())
1249 }
1250
1251 fn queue_decoded_temp(&mut self, input: &[u8], decoded: &mut [u8]) -> io::Result<usize> {
1252 let written = match self.engine.decode_slice(input, decoded) {
1253 Ok(written) => written,
1254 Err(err) => {
1255 crate::wipe_bytes(decoded);
1256 self.failed = true;
1257 return Err(decode_error_to_io(err));
1258 }
1259 };
1260
1261 let result = self.output.push_slice(&decoded[..written]);
1262 crate::wipe_bytes(decoded);
1263 result?;
1264 Ok(written)
1265 }
1266
1267 fn drain_output(&mut self) -> io::Result<()> {
1268 let mut chunk = [0u8; 1024];
1269 while !self.output.is_empty() {
1270 let pending = self.output.copy_front(&mut chunk);
1271 let result = self.inner_mut().write(&chunk[..pending]);
1272 crate::wipe_bytes(&mut chunk[..pending]);
1273 match result {
1274 Ok(0) => {
1275 return Err(io::Error::new(
1276 io::ErrorKind::WriteZero,
1277 "base64 stream decoder could not drain buffered output",
1278 ));
1279 }
1280 Ok(written) => {
1281 if written > pending {
1282 return Err(io::Error::new(
1283 io::ErrorKind::InvalidData,
1284 "wrapped writer reported more bytes than provided",
1285 ));
1286 }
1287 self.output.discard_front(written);
1288 }
1289 Err(err) => return Err(err),
1290 }
1291 }
1292
1293 Ok(())
1294 }
1295 }
1296
1297 impl<W, A, const PAD: bool> Write for Decoder<W, A, PAD>
1298 where
1299 W: Write,
1300 A: Alphabet,
1301 {
1302 fn write(&mut self, input: &[u8]) -> io::Result<usize> {
1303 if self.failed {
1304 return Err(stream_decoder_failed_error());
1305 }
1306 if input.is_empty() {
1307 self.drain_output()?;
1308 return Ok(0);
1309 }
1310 self.drain_output()?;
1311 if self.finalized {
1312 return Err(io::Error::new(
1313 io::ErrorKind::InvalidInput,
1314 "base64 stream decoder received input after finalization",
1315 ));
1316 }
1317 if self.finished {
1318 self.failed = true;
1319 return Err(trailing_input_after_padding_error());
1320 }
1321
1322 let mut consumed = 0;
1323 if self.pending_len > 0 {
1324 let needed = 4 - self.pending_len;
1325 if input.len() < needed {
1326 self.pending[self.pending_len..self.pending_len + input.len()]
1327 .copy_from_slice(input);
1328 self.pending_len += input.len();
1329 return Ok(input.len());
1330 }
1331
1332 let mut quad = [0u8; 4];
1333 quad[..self.pending_len].copy_from_slice(&self.pending[..self.pending_len]);
1334 quad[self.pending_len..].copy_from_slice(&input[..needed]);
1335 let result = self.queue_full_quad(quad);
1336 crate::wipe_bytes(&mut quad);
1337 if let Err(err) = result {
1338 self.clear_pending();
1339 return Err(err);
1340 }
1341 self.clear_pending();
1342 consumed += needed;
1343 return Ok(consumed);
1344 }
1345
1346 let remaining = &input[consumed..];
1347 let full_len = remaining.len() / 4 * 4;
1348 if full_len > 0 {
1349 let quad = [remaining[0], remaining[1], remaining[2], remaining[3]];
1350 let mut quad = quad;
1351 let result = self.queue_full_quad(quad);
1352 crate::wipe_bytes(&mut quad);
1353 result?;
1354 return Ok(consumed + 4);
1355 }
1356
1357 let tail = &remaining[full_len..];
1358 self.pending[..tail.len()].copy_from_slice(tail);
1359 self.pending_len = tail.len();
1360
1361 Ok(input.len())
1362 }
1363
1364 fn flush(&mut self) -> io::Result<()> {
1365 if self.failed {
1366 return Err(stream_decoder_failed_error());
1367 }
1368 self.drain_output()?;
1369 self.inner_mut().flush()
1370 }
1371 }
1372
1373 fn decode_error_to_io(err: DecodeError) -> io::Error {
1374 io::Error::new(io::ErrorKind::InvalidInput, err)
1375 }
1376
1377 fn trailing_input_after_padding_error() -> io::Error {
1378 io::Error::new(
1379 io::ErrorKind::InvalidInput,
1380 "base64 decoder received trailing input after padding",
1381 )
1382 }
1383
1384 fn stream_decoder_failed_error() -> io::Error {
1385 io::Error::new(
1386 io::ErrorKind::InvalidInput,
1387 "base64 stream decoder is failed after malformed input",
1388 )
1389 }
1390
1391 pub struct DecoderReader<R, A, const PAD: bool>
1398 where
1399 A: Alphabet,
1400 {
1401 inner: Option<R>,
1402 engine: Engine<A, PAD>,
1403 pending: [u8; 4],
1404 pending_len: usize,
1405 output: OutputQueue<3>,
1406 finished: bool,
1407 terminal_seen: bool,
1408 failed: bool,
1409 }
1410
1411 impl<R, A, const PAD: bool> DecoderReader<R, A, PAD>
1412 where
1413 A: Alphabet,
1414 {
1415 #[must_use]
1417 pub fn new(inner: R, engine: Engine<A, PAD>) -> Self {
1418 Self {
1419 inner: Some(inner),
1420 engine,
1421 pending: [0; 4],
1422 pending_len: 0,
1423 output: OutputQueue::new(),
1424 finished: false,
1425 terminal_seen: false,
1426 failed: false,
1427 }
1428 }
1429
1430 #[must_use]
1432 pub fn get_ref(&self) -> &R {
1433 self.inner_ref()
1434 }
1435
1436 pub fn get_mut(&mut self) -> &mut R {
1438 self.inner_mut()
1439 }
1440
1441 #[must_use]
1443 pub const fn engine(&self) -> Engine<A, PAD> {
1444 self.engine
1445 }
1446
1447 #[must_use]
1449 pub const fn is_padded(&self) -> bool {
1450 PAD
1451 }
1452
1453 #[must_use]
1456 pub const fn pending_len(&self) -> usize {
1457 self.pending_len
1458 }
1459
1460 #[must_use]
1463 pub const fn has_pending_input(&self) -> bool {
1464 self.pending_len != 0
1465 }
1466
1467 #[must_use]
1472 pub const fn pending_input_needed_len(&self) -> usize {
1473 if self.has_pending_input() {
1474 4 - self.pending_len
1475 } else {
1476 0
1477 }
1478 }
1479
1480 #[must_use]
1483 pub const fn buffered_output_len(&self) -> usize {
1484 self.output.len()
1485 }
1486
1487 #[must_use]
1490 pub const fn buffered_output_capacity(&self) -> usize {
1491 self.output.capacity()
1492 }
1493
1494 #[must_use]
1497 pub const fn buffered_output_remaining_capacity(&self) -> usize {
1498 self.output.available_capacity()
1499 }
1500
1501 #[must_use]
1504 pub const fn has_buffered_output(&self) -> bool {
1505 !self.output.is_empty()
1506 }
1507
1508 #[must_use]
1515 pub const fn has_terminal_padding(&self) -> bool {
1516 self.terminal_seen
1517 }
1518
1519 #[must_use]
1525 pub const fn has_finished_input(&self) -> bool {
1526 self.finished
1527 }
1528
1529 #[must_use]
1532 pub const fn is_finished(&self) -> bool {
1533 self.finished && self.output.is_empty()
1534 }
1535
1536 #[must_use]
1543 pub const fn is_failed(&self) -> bool {
1544 self.failed
1545 }
1546
1547 #[must_use]
1550 pub const fn can_into_inner(&self) -> bool {
1551 !self.is_failed() && self.is_finished()
1552 }
1553
1554 #[must_use]
1556 pub fn into_inner(mut self) -> R {
1557 self.take_inner()
1558 }
1559
1560 #[allow(clippy::result_large_err)]
1568 pub fn try_into_inner(mut self) -> Result<R, Self> {
1569 if !self.can_into_inner() {
1570 return Err(self);
1571 }
1572 Ok(self.take_inner())
1573 }
1574
1575 fn inner_ref(&self) -> &R {
1576 match &self.inner {
1577 Some(inner) => inner,
1578 None => unreachable!("stream decoder reader inner reader was already taken"),
1579 }
1580 }
1581
1582 fn inner_mut(&mut self) -> &mut R {
1583 match &mut self.inner {
1584 Some(inner) => inner,
1585 None => unreachable!("stream decoder reader inner reader was already taken"),
1586 }
1587 }
1588
1589 fn take_inner(&mut self) -> R {
1590 match self.inner.take() {
1591 Some(inner) => inner,
1592 None => unreachable!("stream decoder reader inner reader was already taken"),
1593 }
1594 }
1595
1596 fn clear_pending(&mut self) {
1597 crate::wipe_bytes(&mut self.pending);
1598 self.pending_len = 0;
1599 }
1600 }
1601
1602 impl<R, A, const PAD: bool> Drop for DecoderReader<R, A, PAD>
1603 where
1604 A: Alphabet,
1605 {
1606 fn drop(&mut self) {
1607 self.clear_pending();
1608 self.output.clear_all();
1609 }
1610 }
1611
1612 impl<R, A, const PAD: bool> core::fmt::Debug for DecoderReader<R, A, PAD>
1613 where
1614 A: Alphabet,
1615 {
1616 fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
1617 formatter
1618 .debug_struct("DecoderReader")
1619 .field("inner", &redacted_inner_state(self.inner.is_some()))
1620 .field("engine", &self.engine)
1621 .field("pending", &"<redacted>")
1622 .field("pending_len", &self.pending_len)
1623 .field("pending_input_needed_len", &self.pending_input_needed_len())
1624 .field("buffered_output_len", &self.output.len())
1625 .field("buffered_output_capacity", &self.output.capacity())
1626 .field(
1627 "buffered_output_remaining_capacity",
1628 &self.output.available_capacity(),
1629 )
1630 .field("can_into_inner", &self.can_into_inner())
1631 .field("finished", &self.finished)
1632 .field("terminal_padding", &self.terminal_seen)
1633 .field("failed", &self.failed)
1634 .finish()
1635 }
1636 }
1637
1638 impl<R, A, const PAD: bool> Read for DecoderReader<R, A, PAD>
1639 where
1640 R: Read,
1641 A: Alphabet,
1642 {
1643 fn read(&mut self, output: &mut [u8]) -> io::Result<usize> {
1644 if output.is_empty() {
1645 return Ok(0);
1646 }
1647 if self.failed {
1648 return Err(stream_decoder_failed_error());
1649 }
1650
1651 while self.output.is_empty() && !self.finished {
1652 self.fill_output()?;
1653 }
1654
1655 Ok(self.output.pop_slice(output))
1656 }
1657 }
1658
1659 impl<R, A, const PAD: bool> DecoderReader<R, A, PAD>
1660 where
1661 R: Read,
1662 A: Alphabet,
1663 {
1664 fn fill_output(&mut self) -> io::Result<()> {
1665 if self.failed {
1666 return Err(stream_decoder_failed_error());
1667 }
1668 if self.terminal_seen {
1669 self.finished = true;
1670 return Ok(());
1671 }
1672
1673 let mut input = [0u8; 4];
1674 let available = 4 - self.pending_len;
1675 let read = self.inner_mut().read(&mut input[..available])?;
1676 if read == 0 {
1677 crate::wipe_bytes(&mut input);
1678 self.finished = true;
1679 self.push_final_pending()?;
1680 return Ok(());
1681 }
1682
1683 self.pending[self.pending_len..self.pending_len + read].copy_from_slice(&input[..read]);
1684 crate::wipe_bytes(&mut input);
1685 self.pending_len += read;
1686 if self.pending_len < 4 {
1687 return Ok(());
1688 }
1689
1690 let mut quad = self.pending;
1691 self.clear_pending();
1692 let result = self.push_decoded(&quad);
1693 crate::wipe_bytes(&mut quad);
1694 result?;
1695 if self.terminal_seen {
1696 self.finished = true;
1697 }
1698 Ok(())
1699 }
1700
1701 fn push_final_pending(&mut self) -> io::Result<()> {
1702 if self.pending_len == 0 {
1703 return Ok(());
1704 }
1705
1706 let mut pending = [0u8; 4];
1707 pending[..self.pending_len].copy_from_slice(&self.pending[..self.pending_len]);
1708 let pending_len = self.pending_len;
1709 self.clear_pending();
1710 let result = self.push_decoded(&pending[..pending_len]);
1711 crate::wipe_bytes(&mut pending);
1712 result
1713 }
1714
1715 fn push_decoded(&mut self, input: &[u8]) -> io::Result<()> {
1716 let mut decoded = [0u8; 3];
1717 let written = match self.engine.decode_slice(input, &mut decoded) {
1718 Ok(written) => written,
1719 Err(err) => {
1720 crate::wipe_bytes(&mut decoded);
1721 self.failed = true;
1722 return Err(decode_error_to_io(err));
1723 }
1724 };
1725 let result = self.output.push_slice(&decoded[..written]);
1726 crate::wipe_bytes(&mut decoded);
1727 result?;
1728 if input.len() == 4 && written < 3 {
1729 self.terminal_seen = true;
1730 }
1731 Ok(())
1732 }
1733 }
1734
1735 pub struct EncoderReader<R, A, const PAD: bool>
1737 where
1738 A: Alphabet,
1739 {
1740 inner: Option<R>,
1741 engine: Engine<A, PAD>,
1742 pending: [u8; 2],
1743 pending_len: usize,
1744 output: OutputQueue<1024>,
1745 finished: bool,
1746 }
1747
1748 impl<R, A, const PAD: bool> EncoderReader<R, A, PAD>
1749 where
1750 A: Alphabet,
1751 {
1752 #[must_use]
1754 pub fn new(inner: R, engine: Engine<A, PAD>) -> Self {
1755 Self {
1756 inner: Some(inner),
1757 engine,
1758 pending: [0; 2],
1759 pending_len: 0,
1760 output: OutputQueue::new(),
1761 finished: false,
1762 }
1763 }
1764
1765 #[must_use]
1767 pub fn get_ref(&self) -> &R {
1768 self.inner_ref()
1769 }
1770
1771 pub fn get_mut(&mut self) -> &mut R {
1773 self.inner_mut()
1774 }
1775
1776 #[must_use]
1778 pub const fn engine(&self) -> Engine<A, PAD> {
1779 self.engine
1780 }
1781
1782 #[must_use]
1784 pub const fn is_padded(&self) -> bool {
1785 PAD
1786 }
1787
1788 #[must_use]
1791 pub const fn pending_len(&self) -> usize {
1792 self.pending_len
1793 }
1794
1795 #[must_use]
1798 pub const fn has_pending_input(&self) -> bool {
1799 self.pending_len != 0
1800 }
1801
1802 #[must_use]
1807 pub const fn pending_input_needed_len(&self) -> usize {
1808 if self.has_pending_input() {
1809 3 - self.pending_len
1810 } else {
1811 0
1812 }
1813 }
1814
1815 #[must_use]
1818 pub const fn buffered_output_len(&self) -> usize {
1819 self.output.len()
1820 }
1821
1822 #[must_use]
1825 pub const fn buffered_output_capacity(&self) -> usize {
1826 self.output.capacity()
1827 }
1828
1829 #[must_use]
1832 pub const fn buffered_output_remaining_capacity(&self) -> usize {
1833 self.output.available_capacity()
1834 }
1835
1836 #[must_use]
1839 pub const fn has_buffered_output(&self) -> bool {
1840 !self.output.is_empty()
1841 }
1842
1843 #[must_use]
1849 pub const fn has_finished_input(&self) -> bool {
1850 self.finished
1851 }
1852
1853 #[must_use]
1856 pub const fn is_finished(&self) -> bool {
1857 self.finished && self.output.is_empty()
1858 }
1859
1860 #[must_use]
1863 pub const fn can_into_inner(&self) -> bool {
1864 self.is_finished()
1865 }
1866
1867 #[must_use]
1869 pub fn into_inner(mut self) -> R {
1870 self.take_inner()
1871 }
1872
1873 #[allow(clippy::result_large_err)]
1880 pub fn try_into_inner(mut self) -> Result<R, Self> {
1881 if !self.can_into_inner() {
1882 return Err(self);
1883 }
1884 Ok(self.take_inner())
1885 }
1886
1887 fn inner_ref(&self) -> &R {
1888 match &self.inner {
1889 Some(inner) => inner,
1890 None => unreachable!("stream encoder reader inner reader was already taken"),
1891 }
1892 }
1893
1894 fn inner_mut(&mut self) -> &mut R {
1895 match &mut self.inner {
1896 Some(inner) => inner,
1897 None => unreachable!("stream encoder reader inner reader was already taken"),
1898 }
1899 }
1900
1901 fn take_inner(&mut self) -> R {
1902 match self.inner.take() {
1903 Some(inner) => inner,
1904 None => unreachable!("stream encoder reader inner reader was already taken"),
1905 }
1906 }
1907
1908 fn clear_pending(&mut self) {
1909 crate::wipe_bytes(&mut self.pending);
1910 self.pending_len = 0;
1911 }
1912 }
1913
1914 impl<R, A, const PAD: bool> Drop for EncoderReader<R, A, PAD>
1915 where
1916 A: Alphabet,
1917 {
1918 fn drop(&mut self) {
1919 self.clear_pending();
1920 self.output.clear_all();
1921 }
1922 }
1923
1924 impl<R, A, const PAD: bool> core::fmt::Debug for EncoderReader<R, A, PAD>
1925 where
1926 A: Alphabet,
1927 {
1928 fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
1929 formatter
1930 .debug_struct("EncoderReader")
1931 .field("inner", &redacted_inner_state(self.inner.is_some()))
1932 .field("engine", &self.engine)
1933 .field("pending", &"<redacted>")
1934 .field("pending_len", &self.pending_len)
1935 .field("pending_input_needed_len", &self.pending_input_needed_len())
1936 .field("buffered_output_len", &self.output.len())
1937 .field("buffered_output_capacity", &self.output.capacity())
1938 .field(
1939 "buffered_output_remaining_capacity",
1940 &self.output.available_capacity(),
1941 )
1942 .field("can_into_inner", &self.can_into_inner())
1943 .field("finished", &self.finished)
1944 .finish()
1945 }
1946 }
1947
1948 impl<R, A, const PAD: bool> Read for EncoderReader<R, A, PAD>
1949 where
1950 R: Read,
1951 A: Alphabet,
1952 {
1953 fn read(&mut self, output: &mut [u8]) -> io::Result<usize> {
1954 if output.is_empty() {
1955 return Ok(0);
1956 }
1957
1958 while self.output.is_empty() && !self.finished {
1959 self.fill_output()?;
1960 }
1961
1962 Ok(self.output.pop_slice(output))
1963 }
1964 }
1965
1966 impl<R, A, const PAD: bool> EncoderReader<R, A, PAD>
1967 where
1968 R: Read,
1969 A: Alphabet,
1970 {
1971 fn fill_output(&mut self) -> io::Result<()> {
1972 let mut input = [0u8; 768];
1973 let read = self.inner_mut().read(&mut input)?;
1974 if read == 0 {
1975 crate::wipe_bytes(&mut input);
1976 self.finished = true;
1977 self.push_final_pending()?;
1978 return Ok(());
1979 }
1980
1981 let mut consumed = 0;
1982 if self.pending_len > 0 {
1983 let needed = 3 - self.pending_len;
1984 if read < needed {
1985 self.pending[self.pending_len..self.pending_len + read]
1986 .copy_from_slice(&input[..read]);
1987 self.pending_len += read;
1988 crate::wipe_bytes(&mut input);
1989 return Ok(());
1990 }
1991
1992 let mut chunk = [0u8; 3];
1993 chunk[..self.pending_len].copy_from_slice(&self.pending[..self.pending_len]);
1994 chunk[self.pending_len..].copy_from_slice(&input[..needed]);
1995 let result = self.push_encoded(&chunk);
1996 crate::wipe_bytes(&mut chunk);
1997 if let Err(err) = result {
1998 crate::wipe_bytes(&mut input);
1999 return Err(err);
2000 }
2001 self.clear_pending();
2002 consumed += needed;
2003 }
2004
2005 let remaining = &input[consumed..read];
2006 let full_len = remaining.len() / 3 * 3;
2007 let tail_len = remaining.len() - full_len;
2008 let mut tail = [0u8; 2];
2009 tail[..tail_len].copy_from_slice(&remaining[full_len..]);
2010 let result = if full_len > 0 {
2011 self.push_encoded(&remaining[..full_len])
2012 } else {
2013 Ok(())
2014 };
2015 crate::wipe_bytes(&mut input);
2016 if let Err(err) = result {
2017 crate::wipe_bytes(&mut tail);
2018 return Err(err);
2019 }
2020 self.pending[..tail_len].copy_from_slice(&tail[..tail_len]);
2021 crate::wipe_bytes(&mut tail);
2022 self.pending_len = tail_len;
2023 Ok(())
2024 }
2025
2026 fn push_final_pending(&mut self) -> io::Result<()> {
2027 if self.pending_len == 0 {
2028 return Ok(());
2029 }
2030
2031 let mut pending = [0u8; 2];
2032 pending[..self.pending_len].copy_from_slice(&self.pending[..self.pending_len]);
2033 let pending_len = self.pending_len;
2034 self.clear_pending();
2035 let result = self.push_encoded(&pending[..pending_len]);
2036 crate::wipe_bytes(&mut pending);
2037 result
2038 }
2039
2040 fn push_encoded(&mut self, input: &[u8]) -> io::Result<()> {
2041 let mut encoded = [0u8; 1024];
2042 let written = match self.engine.encode_slice(input, &mut encoded) {
2043 Ok(written) => written,
2044 Err(err) => {
2045 crate::wipe_bytes(&mut encoded);
2046 return Err(encode_error_to_io(err));
2047 }
2048 };
2049 let result = self.output.push_slice(&encoded[..written]);
2050 crate::wipe_bytes(&mut encoded);
2051 result
2052 }
2053 }
2054
2055 const fn redacted_inner_state(present: bool) -> &'static str {
2056 if present { "<present>" } else { "<taken>" }
2057 }
2058}
2059
2060pub mod ct {
2068 use super::{
2069 Alphabet, DecodeError, DecodedBuffer, Standard, UrlSafe, ct_decode_in_place,
2070 ct_decode_slice, ct_decoded_len, ct_validate_decode,
2071 };
2072 use core::marker::PhantomData;
2073
2074 pub const STANDARD: CtEngine<Standard, true> = CtEngine::new();
2076
2077 pub const STANDARD_NO_PAD: CtEngine<Standard, false> = CtEngine::new();
2079
2080 pub const URL_SAFE: CtEngine<UrlSafe, true> = CtEngine::new();
2082
2083 pub const URL_SAFE_NO_PAD: CtEngine<UrlSafe, false> = CtEngine::new();
2085
2086 #[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
2088 pub struct CtEngine<A, const PAD: bool> {
2089 alphabet: PhantomData<A>,
2090 }
2091
2092 impl<A, const PAD: bool> CtEngine<A, PAD>
2093 where
2094 A: Alphabet,
2095 {
2096 #[must_use]
2098 pub const fn new() -> Self {
2099 Self {
2100 alphabet: PhantomData,
2101 }
2102 }
2103
2104 #[must_use]
2107 pub const fn is_padded(&self) -> bool {
2108 PAD
2109 }
2110
2111 pub fn validate_result(&self, input: &[u8]) -> Result<(), DecodeError> {
2126 ct_validate_decode::<A, PAD>(input)
2127 }
2128
2129 #[must_use]
2143 pub fn validate(&self, input: &[u8]) -> bool {
2144 self.validate_result(input).is_ok()
2145 }
2146
2147 pub fn decoded_len(&self, input: &[u8]) -> Result<usize, DecodeError> {
2153 ct_decoded_len::<A, PAD>(input)
2154 }
2155
2156 pub fn decode_slice(&self, input: &[u8], output: &mut [u8]) -> Result<usize, DecodeError> {
2178 ct_decode_slice::<A, PAD>(input, output)
2179 }
2180
2181 pub fn decode_slice_clear_tail(
2203 &self,
2204 input: &[u8],
2205 output: &mut [u8],
2206 ) -> Result<usize, DecodeError> {
2207 let written = match self.decode_slice(input, output) {
2208 Ok(written) => written,
2209 Err(err) => {
2210 crate::wipe_bytes(output);
2211 return Err(err);
2212 }
2213 };
2214 crate::wipe_tail(output, written);
2215 Ok(written)
2216 }
2217
2218 pub fn decode_buffer<const CAP: usize>(
2234 &self,
2235 input: &[u8],
2236 ) -> Result<DecodedBuffer<CAP>, DecodeError> {
2237 let mut output = DecodedBuffer::new();
2238 let written = match self.decode_slice_clear_tail(input, &mut output.bytes) {
2239 Ok(written) => written,
2240 Err(err) => {
2241 output.clear();
2242 return Err(err);
2243 }
2244 };
2245 output.len = written;
2246 Ok(output)
2247 }
2248
2249 pub fn decode_in_place<'a>(
2266 &self,
2267 buffer: &'a mut [u8],
2268 ) -> Result<&'a mut [u8], DecodeError> {
2269 let len = ct_decode_in_place::<A, PAD>(buffer)?;
2270 Ok(&mut buffer[..len])
2271 }
2272
2273 pub fn decode_in_place_clear_tail<'a>(
2290 &self,
2291 buffer: &'a mut [u8],
2292 ) -> Result<&'a mut [u8], DecodeError> {
2293 let len = match ct_decode_in_place::<A, PAD>(buffer) {
2294 Ok(len) => len,
2295 Err(err) => {
2296 crate::wipe_bytes(buffer);
2297 return Err(err);
2298 }
2299 };
2300 crate::wipe_tail(buffer, len);
2301 Ok(&mut buffer[..len])
2302 }
2303 }
2304
2305 impl<A, const PAD: bool> core::fmt::Display for CtEngine<A, PAD> {
2306 fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
2307 write!(formatter, "ct padded={PAD}")
2308 }
2309 }
2310}
2311
2312pub const STANDARD: Engine<Standard, true> = Engine::new();
2314
2315pub const STANDARD_NO_PAD: Engine<Standard, false> = Engine::new();
2317
2318pub const URL_SAFE: Engine<UrlSafe, true> = Engine::new();
2320
2321pub const URL_SAFE_NO_PAD: Engine<UrlSafe, false> = Engine::new();
2323
2324pub const BCRYPT_NO_PAD: Engine<Bcrypt, false> = Engine::new();
2329
2330pub const CRYPT_NO_PAD: Engine<Crypt, false> = Engine::new();
2335
2336#[derive(Clone, Copy, Debug, Eq, PartialEq)]
2338pub enum LineEnding {
2339 Lf,
2341 CrLf,
2343}
2344
2345impl LineEnding {
2346 #[must_use]
2348 pub const fn name(self) -> &'static str {
2349 match self {
2350 Self::Lf => "LF",
2351 Self::CrLf => "CRLF",
2352 }
2353 }
2354
2355 #[must_use]
2357 pub const fn as_str(self) -> &'static str {
2358 match self {
2359 Self::Lf => "\n",
2360 Self::CrLf => "\r\n",
2361 }
2362 }
2363
2364 #[must_use]
2366 pub const fn as_bytes(self) -> &'static [u8] {
2367 self.as_str().as_bytes()
2368 }
2369
2370 #[must_use]
2372 pub const fn byte_len(self) -> usize {
2373 match self {
2374 Self::Lf => 1,
2375 Self::CrLf => 2,
2376 }
2377 }
2378}
2379
2380impl core::fmt::Display for LineEnding {
2381 fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
2382 formatter.write_str(self.name())
2383 }
2384}
2385
2386#[derive(Clone, Copy, Debug, Eq, PartialEq)]
2392pub struct LineWrap {
2393 pub line_len: usize,
2395 pub line_ending: LineEnding,
2397}
2398
2399impl LineWrap {
2400 pub const MIME: Self = Self::new(76, LineEnding::CrLf);
2402 pub const PEM: Self = Self::new(64, LineEnding::Lf);
2404 pub const PEM_CRLF: Self = Self::new(64, LineEnding::CrLf);
2406
2407 #[must_use]
2409 pub const fn new(line_len: usize, line_ending: LineEnding) -> Self {
2410 Self {
2411 line_len,
2412 line_ending,
2413 }
2414 }
2415
2416 #[must_use]
2423 pub const fn checked_new(line_len: usize, line_ending: LineEnding) -> Option<Self> {
2424 if line_len == 0 {
2425 None
2426 } else {
2427 Some(Self::new(line_len, line_ending))
2428 }
2429 }
2430
2431 #[must_use]
2433 pub const fn line_len(self) -> usize {
2434 self.line_len
2435 }
2436
2437 #[must_use]
2439 pub const fn line_ending(self) -> LineEnding {
2440 self.line_ending
2441 }
2442
2443 #[must_use]
2445 pub const fn is_valid(self) -> bool {
2446 self.line_len != 0
2447 }
2448}
2449
2450impl core::fmt::Display for LineWrap {
2451 fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
2452 write!(formatter, "{}:{}", self.line_len, self.line_ending.name())
2453 }
2454}
2455
2456#[allow(unsafe_code)]
2457fn wipe_bytes(bytes: &mut [u8]) {
2458 for byte in bytes {
2459 unsafe {
2463 core::ptr::write_volatile(byte, 0);
2464 }
2465 }
2466 core::sync::atomic::compiler_fence(core::sync::atomic::Ordering::SeqCst);
2467}
2468
2469fn wipe_tail(bytes: &mut [u8], start: usize) {
2470 wipe_bytes(&mut bytes[start..]);
2471}
2472
2473#[cfg(feature = "alloc")]
2474#[allow(unsafe_code)]
2475fn wipe_vec_spare_capacity(bytes: &mut alloc::vec::Vec<u8>) {
2476 let ptr = bytes.as_mut_ptr();
2477 let mut offset = bytes.len();
2478 while offset < bytes.capacity() {
2479 unsafe {
2483 core::ptr::write_volatile(ptr.add(offset), 0);
2484 }
2485 offset += 1;
2486 }
2487 core::sync::atomic::compiler_fence(core::sync::atomic::Ordering::SeqCst);
2488}
2489
2490#[cfg(feature = "alloc")]
2491fn wipe_vec_all(bytes: &mut alloc::vec::Vec<u8>) {
2492 wipe_bytes(bytes);
2493 wipe_vec_spare_capacity(bytes);
2494}
2495
2496pub struct EncodedBuffer<const CAP: usize> {
2506 bytes: [u8; CAP],
2507 len: usize,
2508}
2509
2510impl<const CAP: usize> EncodedBuffer<CAP> {
2511 #[must_use]
2513 pub const fn new() -> Self {
2514 Self {
2515 bytes: [0u8; CAP],
2516 len: 0,
2517 }
2518 }
2519
2520 #[must_use]
2522 pub const fn len(&self) -> usize {
2523 self.len
2524 }
2525
2526 #[must_use]
2528 pub const fn is_empty(&self) -> bool {
2529 self.len == 0
2530 }
2531
2532 #[must_use]
2534 pub const fn is_full(&self) -> bool {
2535 self.len == CAP
2536 }
2537
2538 #[must_use]
2540 pub const fn capacity(&self) -> usize {
2541 CAP
2542 }
2543
2544 #[must_use]
2546 pub const fn remaining_capacity(&self) -> usize {
2547 CAP - self.len
2548 }
2549
2550 #[must_use]
2552 pub fn as_bytes(&self) -> &[u8] {
2553 &self.bytes[..self.len]
2554 }
2555
2556 pub fn as_utf8(&self) -> Result<&str, core::str::Utf8Error> {
2563 core::str::from_utf8(self.as_bytes())
2564 }
2565
2566 #[must_use]
2573 pub fn as_str(&self) -> &str {
2574 match self.as_utf8() {
2575 Ok(output) => output,
2576 Err(_) => unreachable!("base64 encoder produced non-UTF-8 output"),
2577 }
2578 }
2579
2580 #[must_use]
2588 pub fn constant_time_eq(&self, other: &[u8]) -> bool {
2589 constant_time_eq_public_len(self.as_bytes(), other)
2590 }
2591
2592 #[must_use]
2600 pub fn into_exposed_array(mut self) -> ([u8; CAP], usize) {
2601 let len = self.len;
2602 self.len = 0;
2603 (core::mem::replace(&mut self.bytes, [0u8; CAP]), len)
2604 }
2605
2606 pub fn clear(&mut self) {
2608 wipe_bytes(&mut self.bytes);
2609 self.len = 0;
2610 }
2611
2612 pub fn clear_tail(&mut self) {
2614 wipe_tail(&mut self.bytes, self.len);
2615 }
2616}
2617
2618impl<const CAP: usize> AsRef<[u8]> for EncodedBuffer<CAP> {
2619 fn as_ref(&self) -> &[u8] {
2620 self.as_bytes()
2621 }
2622}
2623
2624impl<const CAP: usize> Clone for EncodedBuffer<CAP> {
2625 fn clone(&self) -> Self {
2626 let mut output = Self::new();
2627 output.bytes[..self.len].copy_from_slice(self.as_bytes());
2628 output.len = self.len;
2629 output
2630 }
2631}
2632
2633impl<const CAP: usize> core::fmt::Debug for EncodedBuffer<CAP> {
2634 fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
2635 formatter
2636 .debug_struct("EncodedBuffer")
2637 .field("bytes", &"<redacted>")
2638 .field("len", &self.len)
2639 .field("capacity", &CAP)
2640 .finish()
2641 }
2642}
2643
2644impl<const CAP: usize> core::fmt::Display for EncodedBuffer<CAP> {
2645 fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
2646 formatter.write_str(self.as_str())
2647 }
2648}
2649
2650impl<const CAP: usize> Default for EncodedBuffer<CAP> {
2651 fn default() -> Self {
2652 Self::new()
2653 }
2654}
2655
2656impl<const CAP: usize> Drop for EncodedBuffer<CAP> {
2657 fn drop(&mut self) {
2658 self.clear();
2659 }
2660}
2661
2662impl<const CAP: usize> Eq for EncodedBuffer<CAP> {}
2663
2664impl<const CAP: usize> PartialEq for EncodedBuffer<CAP> {
2665 fn eq(&self, other: &Self) -> bool {
2666 self.constant_time_eq(other.as_bytes())
2667 }
2668}
2669
2670impl<const CAP: usize> PartialEq<&[u8]> for EncodedBuffer<CAP> {
2671 fn eq(&self, other: &&[u8]) -> bool {
2672 self.constant_time_eq(other)
2673 }
2674}
2675
2676impl<const CAP: usize, const N: usize> PartialEq<&[u8; N]> for EncodedBuffer<CAP> {
2677 fn eq(&self, other: &&[u8; N]) -> bool {
2678 self.constant_time_eq(&other[..])
2679 }
2680}
2681
2682impl<const CAP: usize> PartialEq<&str> for EncodedBuffer<CAP> {
2683 fn eq(&self, other: &&str) -> bool {
2684 self.constant_time_eq(other.as_bytes())
2685 }
2686}
2687
2688#[cfg(feature = "alloc")]
2689impl<const CAP: usize> PartialEq<alloc::string::String> for EncodedBuffer<CAP> {
2690 fn eq(&self, other: &alloc::string::String) -> bool {
2691 self.constant_time_eq(other.as_bytes())
2692 }
2693}
2694
2695impl<const CAP: usize> PartialEq<EncodedBuffer<CAP>> for &[u8] {
2696 fn eq(&self, other: &EncodedBuffer<CAP>) -> bool {
2697 other.constant_time_eq(self)
2698 }
2699}
2700
2701impl<const CAP: usize, const N: usize> PartialEq<EncodedBuffer<CAP>> for &[u8; N] {
2702 fn eq(&self, other: &EncodedBuffer<CAP>) -> bool {
2703 other.constant_time_eq(&self[..])
2704 }
2705}
2706
2707impl<const CAP: usize> PartialEq<EncodedBuffer<CAP>> for &str {
2708 fn eq(&self, other: &EncodedBuffer<CAP>) -> bool {
2709 other.constant_time_eq(self.as_bytes())
2710 }
2711}
2712
2713#[cfg(feature = "alloc")]
2714impl<const CAP: usize> PartialEq<EncodedBuffer<CAP>> for alloc::string::String {
2715 fn eq(&self, other: &EncodedBuffer<CAP>) -> bool {
2716 other.constant_time_eq(self.as_bytes())
2717 }
2718}
2719
2720impl<const CAP: usize> TryFrom<&[u8]> for EncodedBuffer<CAP> {
2721 type Error = EncodeError;
2722
2723 fn try_from(input: &[u8]) -> Result<Self, Self::Error> {
2729 STANDARD.encode_buffer(input)
2730 }
2731}
2732
2733impl<const CAP: usize, const N: usize> TryFrom<&[u8; N]> for EncodedBuffer<CAP> {
2734 type Error = EncodeError;
2735
2736 fn try_from(input: &[u8; N]) -> Result<Self, Self::Error> {
2742 Self::try_from(&input[..])
2743 }
2744}
2745
2746impl<const CAP: usize> TryFrom<&str> for EncodedBuffer<CAP> {
2747 type Error = EncodeError;
2748
2749 fn try_from(input: &str) -> Result<Self, Self::Error> {
2756 Self::try_from(input.as_bytes())
2757 }
2758}
2759
2760pub struct DecodedBuffer<const CAP: usize> {
2770 bytes: [u8; CAP],
2771 len: usize,
2772}
2773
2774impl<const CAP: usize> DecodedBuffer<CAP> {
2775 #[must_use]
2777 pub const fn new() -> Self {
2778 Self {
2779 bytes: [0u8; CAP],
2780 len: 0,
2781 }
2782 }
2783
2784 #[must_use]
2786 pub const fn len(&self) -> usize {
2787 self.len
2788 }
2789
2790 #[must_use]
2792 pub const fn is_empty(&self) -> bool {
2793 self.len == 0
2794 }
2795
2796 #[must_use]
2798 pub const fn is_full(&self) -> bool {
2799 self.len == CAP
2800 }
2801
2802 #[must_use]
2804 pub const fn capacity(&self) -> usize {
2805 CAP
2806 }
2807
2808 #[must_use]
2810 pub const fn remaining_capacity(&self) -> usize {
2811 CAP - self.len
2812 }
2813
2814 #[must_use]
2816 pub fn as_bytes(&self) -> &[u8] {
2817 &self.bytes[..self.len]
2818 }
2819
2820 pub fn as_utf8(&self) -> Result<&str, core::str::Utf8Error> {
2826 core::str::from_utf8(self.as_bytes())
2827 }
2828
2829 #[must_use]
2837 pub fn constant_time_eq(&self, other: &[u8]) -> bool {
2838 constant_time_eq_public_len(self.as_bytes(), other)
2839 }
2840
2841 #[must_use]
2849 pub fn into_exposed_array(mut self) -> ([u8; CAP], usize) {
2850 let len = self.len;
2851 self.len = 0;
2852 (core::mem::replace(&mut self.bytes, [0u8; CAP]), len)
2853 }
2854
2855 pub fn clear(&mut self) {
2857 wipe_bytes(&mut self.bytes);
2858 self.len = 0;
2859 }
2860
2861 pub fn clear_tail(&mut self) {
2863 wipe_tail(&mut self.bytes, self.len);
2864 }
2865}
2866
2867impl<const CAP: usize> AsRef<[u8]> for DecodedBuffer<CAP> {
2868 fn as_ref(&self) -> &[u8] {
2869 self.as_bytes()
2870 }
2871}
2872
2873impl<const CAP: usize> Clone for DecodedBuffer<CAP> {
2874 fn clone(&self) -> Self {
2875 let mut output = Self::new();
2876 output.bytes[..self.len].copy_from_slice(self.as_bytes());
2877 output.len = self.len;
2878 output
2879 }
2880}
2881
2882impl<const CAP: usize> core::fmt::Debug for DecodedBuffer<CAP> {
2883 fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
2884 formatter
2885 .debug_struct("DecodedBuffer")
2886 .field("bytes", &"<redacted>")
2887 .field("len", &self.len)
2888 .field("capacity", &CAP)
2889 .finish()
2890 }
2891}
2892
2893impl<const CAP: usize> Default for DecodedBuffer<CAP> {
2894 fn default() -> Self {
2895 Self::new()
2896 }
2897}
2898
2899impl<const CAP: usize> Drop for DecodedBuffer<CAP> {
2900 fn drop(&mut self) {
2901 self.clear();
2902 }
2903}
2904
2905impl<const CAP: usize> Eq for DecodedBuffer<CAP> {}
2906
2907impl<const CAP: usize> PartialEq for DecodedBuffer<CAP> {
2908 fn eq(&self, other: &Self) -> bool {
2909 self.constant_time_eq(other.as_bytes())
2910 }
2911}
2912
2913impl<const CAP: usize> PartialEq<&[u8]> for DecodedBuffer<CAP> {
2914 fn eq(&self, other: &&[u8]) -> bool {
2915 self.constant_time_eq(other)
2916 }
2917}
2918
2919impl<const CAP: usize, const N: usize> PartialEq<&[u8; N]> for DecodedBuffer<CAP> {
2920 fn eq(&self, other: &&[u8; N]) -> bool {
2921 self.constant_time_eq(&other[..])
2922 }
2923}
2924
2925impl<const CAP: usize> PartialEq<&str> for DecodedBuffer<CAP> {
2926 fn eq(&self, other: &&str) -> bool {
2927 self.constant_time_eq(other.as_bytes())
2928 }
2929}
2930
2931#[cfg(feature = "alloc")]
2932impl<const CAP: usize> PartialEq<alloc::string::String> for DecodedBuffer<CAP> {
2933 fn eq(&self, other: &alloc::string::String) -> bool {
2934 self.constant_time_eq(other.as_bytes())
2935 }
2936}
2937
2938impl<const CAP: usize> PartialEq<DecodedBuffer<CAP>> for &[u8] {
2939 fn eq(&self, other: &DecodedBuffer<CAP>) -> bool {
2940 other.constant_time_eq(self)
2941 }
2942}
2943
2944impl<const CAP: usize, const N: usize> PartialEq<DecodedBuffer<CAP>> for &[u8; N] {
2945 fn eq(&self, other: &DecodedBuffer<CAP>) -> bool {
2946 other.constant_time_eq(&self[..])
2947 }
2948}
2949
2950impl<const CAP: usize> PartialEq<DecodedBuffer<CAP>> for &str {
2951 fn eq(&self, other: &DecodedBuffer<CAP>) -> bool {
2952 other.constant_time_eq(self.as_bytes())
2953 }
2954}
2955
2956#[cfg(feature = "alloc")]
2957impl<const CAP: usize> PartialEq<DecodedBuffer<CAP>> for alloc::string::String {
2958 fn eq(&self, other: &DecodedBuffer<CAP>) -> bool {
2959 other.constant_time_eq(self.as_bytes())
2960 }
2961}
2962
2963impl<const CAP: usize> TryFrom<&[u8]> for DecodedBuffer<CAP> {
2964 type Error = DecodeError;
2965
2966 fn try_from(input: &[u8]) -> Result<Self, Self::Error> {
2971 STANDARD.decode_buffer(input)
2972 }
2973}
2974
2975impl<const CAP: usize, const N: usize> TryFrom<&[u8; N]> for DecodedBuffer<CAP> {
2976 type Error = DecodeError;
2977
2978 fn try_from(input: &[u8; N]) -> Result<Self, Self::Error> {
2984 Self::try_from(&input[..])
2985 }
2986}
2987
2988impl<const CAP: usize> TryFrom<&str> for DecodedBuffer<CAP> {
2989 type Error = DecodeError;
2990
2991 fn try_from(input: &str) -> Result<Self, Self::Error> {
2996 Self::try_from(input.as_bytes())
2997 }
2998}
2999
3000impl<const CAP: usize> core::str::FromStr for DecodedBuffer<CAP> {
3001 type Err = DecodeError;
3002
3003 fn from_str(input: &str) -> Result<Self, Self::Err> {
3008 Self::try_from(input)
3009 }
3010}
3011
3012#[cfg(feature = "alloc")]
3024pub struct SecretBuffer {
3025 bytes: alloc::vec::Vec<u8>,
3026}
3027
3028#[cfg(feature = "alloc")]
3029impl SecretBuffer {
3030 #[must_use]
3032 pub fn from_vec(mut bytes: alloc::vec::Vec<u8>) -> Self {
3033 wipe_vec_spare_capacity(&mut bytes);
3034 Self { bytes }
3035 }
3036
3037 #[must_use]
3039 pub fn from_slice(bytes: &[u8]) -> Self {
3040 Self::from_vec(bytes.to_vec())
3041 }
3042
3043 #[must_use]
3045 pub fn len(&self) -> usize {
3046 self.bytes.len()
3047 }
3048
3049 #[must_use]
3051 pub fn is_empty(&self) -> bool {
3052 self.bytes.is_empty()
3053 }
3054
3055 #[must_use]
3060 pub fn expose_secret(&self) -> &[u8] {
3061 &self.bytes
3062 }
3063
3064 pub fn expose_secret_utf8(&self) -> Result<&str, core::str::Utf8Error> {
3070 core::str::from_utf8(self.expose_secret())
3071 }
3072
3073 #[must_use]
3078 pub fn expose_secret_mut(&mut self) -> &mut [u8] {
3079 &mut self.bytes
3080 }
3081
3082 #[must_use]
3089 pub fn into_exposed_vec(mut self) -> alloc::vec::Vec<u8> {
3090 core::mem::take(&mut self.bytes)
3091 }
3092
3093 pub fn try_into_exposed_string(self) -> Result<alloc::string::String, Self> {
3103 if core::str::from_utf8(self.expose_secret()).is_err() {
3104 return Err(self);
3105 }
3106
3107 match alloc::string::String::from_utf8(self.into_exposed_vec()) {
3108 Ok(text) => Ok(text),
3109 Err(error) => Err(Self::from_vec(error.into_bytes())),
3110 }
3111 }
3112
3113 #[must_use]
3121 pub fn constant_time_eq(&self, other: &[u8]) -> bool {
3122 constant_time_eq_public_len(self.expose_secret(), other)
3123 }
3124
3125 pub fn clear(&mut self) {
3127 wipe_vec_all(&mut self.bytes);
3128 self.bytes.clear();
3129 }
3130}
3131
3132#[cfg(feature = "alloc")]
3133impl Clone for SecretBuffer {
3134 fn clone(&self) -> Self {
3135 Self::from_slice(self.expose_secret())
3136 }
3137}
3138
3139#[cfg(feature = "alloc")]
3140impl core::fmt::Debug for SecretBuffer {
3141 fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
3142 formatter
3143 .debug_struct("SecretBuffer")
3144 .field("bytes", &"<redacted>")
3145 .field("len", &self.len())
3146 .finish()
3147 }
3148}
3149
3150#[cfg(feature = "alloc")]
3151impl core::fmt::Display for SecretBuffer {
3152 fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
3153 formatter.write_str("<redacted>")
3154 }
3155}
3156
3157#[cfg(feature = "alloc")]
3158impl Drop for SecretBuffer {
3159 fn drop(&mut self) {
3160 wipe_vec_all(&mut self.bytes);
3161 }
3162}
3163
3164#[cfg(feature = "alloc")]
3165impl Eq for SecretBuffer {}
3166
3167#[cfg(feature = "alloc")]
3168impl PartialEq for SecretBuffer {
3169 fn eq(&self, other: &Self) -> bool {
3170 self.constant_time_eq(other.expose_secret())
3171 }
3172}
3173
3174#[cfg(feature = "alloc")]
3175impl PartialEq<&[u8]> for SecretBuffer {
3176 fn eq(&self, other: &&[u8]) -> bool {
3177 self.constant_time_eq(other)
3178 }
3179}
3180
3181#[cfg(feature = "alloc")]
3182impl<const N: usize> PartialEq<&[u8; N]> for SecretBuffer {
3183 fn eq(&self, other: &&[u8; N]) -> bool {
3184 self.constant_time_eq(&other[..])
3185 }
3186}
3187
3188#[cfg(feature = "alloc")]
3189impl PartialEq<&str> for SecretBuffer {
3190 fn eq(&self, other: &&str) -> bool {
3191 self.constant_time_eq(other.as_bytes())
3192 }
3193}
3194
3195#[cfg(feature = "alloc")]
3196impl PartialEq<alloc::string::String> for SecretBuffer {
3197 fn eq(&self, other: &alloc::string::String) -> bool {
3198 self.constant_time_eq(other.as_bytes())
3199 }
3200}
3201
3202#[cfg(feature = "alloc")]
3203impl PartialEq<SecretBuffer> for &[u8] {
3204 fn eq(&self, other: &SecretBuffer) -> bool {
3205 other.constant_time_eq(self)
3206 }
3207}
3208
3209#[cfg(feature = "alloc")]
3210impl<const N: usize> PartialEq<SecretBuffer> for &[u8; N] {
3211 fn eq(&self, other: &SecretBuffer) -> bool {
3212 other.constant_time_eq(&self[..])
3213 }
3214}
3215
3216#[cfg(feature = "alloc")]
3217impl PartialEq<SecretBuffer> for &str {
3218 fn eq(&self, other: &SecretBuffer) -> bool {
3219 other.constant_time_eq(self.as_bytes())
3220 }
3221}
3222
3223#[cfg(feature = "alloc")]
3224impl PartialEq<SecretBuffer> for alloc::string::String {
3225 fn eq(&self, other: &SecretBuffer) -> bool {
3226 other.constant_time_eq(self.as_bytes())
3227 }
3228}
3229
3230#[cfg(feature = "alloc")]
3231impl From<alloc::vec::Vec<u8>> for SecretBuffer {
3232 fn from(bytes: alloc::vec::Vec<u8>) -> Self {
3237 Self::from_vec(bytes)
3238 }
3239}
3240
3241#[cfg(feature = "alloc")]
3242impl From<alloc::string::String> for SecretBuffer {
3243 fn from(text: alloc::string::String) -> Self {
3248 Self::from_vec(text.into_bytes())
3249 }
3250}
3251
3252#[cfg(feature = "alloc")]
3253impl<const CAP: usize> From<EncodedBuffer<CAP>> for SecretBuffer {
3254 fn from(buffer: EncodedBuffer<CAP>) -> Self {
3260 Self::from_slice(buffer.as_bytes())
3261 }
3262}
3263
3264#[cfg(feature = "alloc")]
3265impl<const CAP: usize> From<DecodedBuffer<CAP>> for SecretBuffer {
3266 fn from(buffer: DecodedBuffer<CAP>) -> Self {
3272 Self::from_slice(buffer.as_bytes())
3273 }
3274}
3275
3276#[cfg(feature = "alloc")]
3277impl TryFrom<&[u8]> for SecretBuffer {
3278 type Error = DecodeError;
3279
3280 fn try_from(input: &[u8]) -> Result<Self, Self::Error> {
3285 STANDARD.decode_secret(input)
3286 }
3287}
3288
3289#[cfg(feature = "alloc")]
3290impl<const N: usize> TryFrom<&[u8; N]> for SecretBuffer {
3291 type Error = DecodeError;
3292
3293 fn try_from(input: &[u8; N]) -> Result<Self, Self::Error> {
3299 Self::try_from(&input[..])
3300 }
3301}
3302
3303#[cfg(feature = "alloc")]
3304impl TryFrom<&str> for SecretBuffer {
3305 type Error = DecodeError;
3306
3307 fn try_from(input: &str) -> Result<Self, Self::Error> {
3312 Self::try_from(input.as_bytes())
3313 }
3314}
3315
3316#[cfg(feature = "alloc")]
3317impl core::str::FromStr for SecretBuffer {
3318 type Err = DecodeError;
3319
3320 fn from_str(input: &str) -> Result<Self, Self::Err> {
3325 Self::try_from(input)
3326 }
3327}
3328
3329#[derive(Clone, Copy, Debug, Eq, PartialEq)]
3335pub struct Profile<A, const PAD: bool> {
3336 engine: Engine<A, PAD>,
3337 wrap: Option<LineWrap>,
3338}
3339
3340impl<A, const PAD: bool> Profile<A, PAD>
3341where
3342 A: Alphabet,
3343{
3344 #[must_use]
3346 pub const fn new(engine: Engine<A, PAD>, wrap: Option<LineWrap>) -> Self {
3347 Self { engine, wrap }
3348 }
3349
3350 #[must_use]
3356 pub const fn checked_new(engine: Engine<A, PAD>, wrap: Option<LineWrap>) -> Option<Self> {
3357 match wrap {
3358 Some(wrap) if !wrap.is_valid() => None,
3359 _ => Some(Self::new(engine, wrap)),
3360 }
3361 }
3362
3363 #[must_use]
3365 pub const fn is_valid(&self) -> bool {
3366 match self.wrap {
3367 Some(wrap) => wrap.is_valid(),
3368 None => true,
3369 }
3370 }
3371
3372 #[must_use]
3374 pub const fn engine(&self) -> Engine<A, PAD> {
3375 self.engine
3376 }
3377
3378 #[must_use]
3380 pub const fn is_padded(&self) -> bool {
3381 PAD
3382 }
3383
3384 #[must_use]
3386 pub const fn is_wrapped(&self) -> bool {
3387 self.wrap.is_some()
3388 }
3389
3390 #[must_use]
3392 pub const fn line_wrap(&self) -> Option<LineWrap> {
3393 self.wrap
3394 }
3395
3396 #[must_use]
3398 pub const fn line_len(&self) -> Option<usize> {
3399 match self.wrap {
3400 Some(wrap) => Some(wrap.line_len()),
3401 None => None,
3402 }
3403 }
3404
3405 #[must_use]
3407 pub const fn line_ending(&self) -> Option<LineEnding> {
3408 match self.wrap {
3409 Some(wrap) => Some(wrap.line_ending()),
3410 None => None,
3411 }
3412 }
3413
3414 pub const fn encoded_len(&self, input_len: usize) -> Result<usize, EncodeError> {
3416 match self.wrap {
3417 Some(wrap) => wrapped_encoded_len(input_len, PAD, wrap),
3418 None => encoded_len(input_len, PAD),
3419 }
3420 }
3421
3422 #[must_use]
3425 pub const fn checked_encoded_len(&self, input_len: usize) -> Option<usize> {
3426 match self.wrap {
3427 Some(wrap) => checked_wrapped_encoded_len(input_len, PAD, wrap),
3428 None => checked_encoded_len(input_len, PAD),
3429 }
3430 }
3431
3432 pub fn decoded_len(&self, input: &[u8]) -> Result<usize, DecodeError> {
3434 match self.wrap {
3435 Some(wrap) => self.engine.decoded_len_wrapped(input, wrap),
3436 None => self.engine.decoded_len(input),
3437 }
3438 }
3439
3440 pub fn validate_result(&self, input: &[u8]) -> Result<(), DecodeError> {
3442 match self.wrap {
3443 Some(wrap) => self.engine.validate_wrapped_result(input, wrap),
3444 None => self.engine.validate_result(input),
3445 }
3446 }
3447
3448 #[must_use]
3450 pub fn validate(&self, input: &[u8]) -> bool {
3451 self.validate_result(input).is_ok()
3452 }
3453
3454 pub fn encode_slice(&self, input: &[u8], output: &mut [u8]) -> Result<usize, EncodeError> {
3456 match self.wrap {
3457 Some(wrap) => self.engine.encode_slice_wrapped(input, output, wrap),
3458 None => self.engine.encode_slice(input, output),
3459 }
3460 }
3461
3462 pub fn encode_slice_clear_tail(
3465 &self,
3466 input: &[u8],
3467 output: &mut [u8],
3468 ) -> Result<usize, EncodeError> {
3469 match self.wrap {
3470 Some(wrap) => self
3471 .engine
3472 .encode_slice_wrapped_clear_tail(input, output, wrap),
3473 None => self.engine.encode_slice_clear_tail(input, output),
3474 }
3475 }
3476
3477 pub fn encode_buffer<const CAP: usize>(
3483 &self,
3484 input: &[u8],
3485 ) -> Result<EncodedBuffer<CAP>, EncodeError> {
3486 let mut output = EncodedBuffer::new();
3487 let written = match self.encode_slice_clear_tail(input, &mut output.bytes) {
3488 Ok(written) => written,
3489 Err(err) => {
3490 output.clear();
3491 return Err(err);
3492 }
3493 };
3494 output.len = written;
3495 Ok(output)
3496 }
3497
3498 pub fn decode_slice(&self, input: &[u8], output: &mut [u8]) -> Result<usize, DecodeError> {
3500 match self.wrap {
3501 Some(wrap) => self.engine.decode_slice_wrapped(input, output, wrap),
3502 None => self.engine.decode_slice(input, output),
3503 }
3504 }
3505
3506 pub fn decode_slice_clear_tail(
3509 &self,
3510 input: &[u8],
3511 output: &mut [u8],
3512 ) -> Result<usize, DecodeError> {
3513 match self.wrap {
3514 Some(wrap) => self
3515 .engine
3516 .decode_slice_wrapped_clear_tail(input, output, wrap),
3517 None => self.engine.decode_slice_clear_tail(input, output),
3518 }
3519 }
3520
3521 pub fn decode_buffer<const CAP: usize>(
3527 &self,
3528 input: &[u8],
3529 ) -> Result<DecodedBuffer<CAP>, DecodeError> {
3530 let mut output = DecodedBuffer::new();
3531 let written = match self.decode_slice_clear_tail(input, &mut output.bytes) {
3532 Ok(written) => written,
3533 Err(err) => {
3534 output.clear();
3535 return Err(err);
3536 }
3537 };
3538 output.len = written;
3539 Ok(output)
3540 }
3541
3542 pub fn decode_in_place<'a>(&self, buffer: &'a mut [u8]) -> Result<&'a mut [u8], DecodeError> {
3559 match self.wrap {
3560 Some(wrap) => self.engine.decode_in_place_wrapped(buffer, wrap),
3561 None => self.engine.decode_in_place(buffer),
3562 }
3563 }
3564
3565 pub fn decode_in_place_clear_tail<'a>(
3584 &self,
3585 buffer: &'a mut [u8],
3586 ) -> Result<&'a mut [u8], DecodeError> {
3587 match self.wrap {
3588 Some(wrap) => self.engine.decode_in_place_wrapped_clear_tail(buffer, wrap),
3589 None => self.engine.decode_in_place_clear_tail(buffer),
3590 }
3591 }
3592
3593 #[cfg(feature = "alloc")]
3595 pub fn encode_vec(&self, input: &[u8]) -> Result<alloc::vec::Vec<u8>, EncodeError> {
3596 match self.wrap {
3597 Some(wrap) => self.engine.encode_wrapped_vec(input, wrap),
3598 None => self.engine.encode_vec(input),
3599 }
3600 }
3601
3602 #[cfg(feature = "alloc")]
3604 pub fn encode_secret(&self, input: &[u8]) -> Result<SecretBuffer, EncodeError> {
3605 self.encode_vec(input).map(SecretBuffer::from_vec)
3606 }
3607
3608 #[cfg(feature = "alloc")]
3610 pub fn encode_string(&self, input: &[u8]) -> Result<alloc::string::String, EncodeError> {
3611 match self.wrap {
3612 Some(wrap) => self.engine.encode_wrapped_string(input, wrap),
3613 None => self.engine.encode_string(input),
3614 }
3615 }
3616
3617 #[cfg(feature = "alloc")]
3619 pub fn decode_vec(&self, input: &[u8]) -> Result<alloc::vec::Vec<u8>, DecodeError> {
3620 match self.wrap {
3621 Some(wrap) => self.engine.decode_wrapped_vec(input, wrap),
3622 None => self.engine.decode_vec(input),
3623 }
3624 }
3625
3626 #[cfg(feature = "alloc")]
3628 pub fn decode_secret(&self, input: &[u8]) -> Result<SecretBuffer, DecodeError> {
3629 self.decode_vec(input).map(SecretBuffer::from_vec)
3630 }
3631}
3632
3633impl<A, const PAD: bool> Default for Profile<A, PAD>
3634where
3635 A: Alphabet,
3636{
3637 fn default() -> Self {
3638 Self::new(Engine::new(), None)
3639 }
3640}
3641
3642impl<A, const PAD: bool> core::fmt::Display for Profile<A, PAD>
3643where
3644 A: Alphabet,
3645{
3646 fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
3647 match self.wrap {
3648 Some(wrap) => write!(formatter, "padded={PAD} wrap={wrap}"),
3649 None => write!(formatter, "padded={PAD} wrap=none"),
3650 }
3651 }
3652}
3653
3654impl<A, const PAD: bool> From<Engine<A, PAD>> for Profile<A, PAD>
3655where
3656 A: Alphabet,
3657{
3658 fn from(engine: Engine<A, PAD>) -> Self {
3659 Self::new(engine, None)
3660 }
3661}
3662
3663pub const MIME: Profile<Standard, true> = Profile::new(STANDARD, Some(LineWrap::MIME));
3665
3666pub const PEM: Profile<Standard, true> = Profile::new(STANDARD, Some(LineWrap::PEM));
3668
3669pub const PEM_CRLF: Profile<Standard, true> = Profile::new(STANDARD, Some(LineWrap::PEM_CRLF));
3671
3672pub const BCRYPT: Profile<Bcrypt, false> = Profile::new(BCRYPT_NO_PAD, None);
3677
3678pub const CRYPT: Profile<Crypt, false> = Profile::new(CRYPT_NO_PAD, None);
3683
3684pub const fn encoded_len(input_len: usize, padded: bool) -> Result<usize, EncodeError> {
3699 match checked_encoded_len(input_len, padded) {
3700 Some(len) => Ok(len),
3701 None => Err(EncodeError::LengthOverflow),
3702 }
3703}
3704
3705pub const fn wrapped_encoded_len(
3719 input_len: usize,
3720 padded: bool,
3721 wrap: LineWrap,
3722) -> Result<usize, EncodeError> {
3723 if wrap.line_len == 0 {
3724 return Err(EncodeError::InvalidLineWrap { line_len: 0 });
3725 }
3726
3727 let Some(encoded) = checked_encoded_len(input_len, padded) else {
3728 return Err(EncodeError::LengthOverflow);
3729 };
3730 if encoded == 0 {
3731 return Ok(0);
3732 }
3733
3734 let breaks = (encoded - 1) / wrap.line_len;
3735 let Some(line_ending_bytes) = breaks.checked_mul(wrap.line_ending.byte_len()) else {
3736 return Err(EncodeError::LengthOverflow);
3737 };
3738 match encoded.checked_add(line_ending_bytes) {
3739 Some(len) => Ok(len),
3740 None => Err(EncodeError::LengthOverflow),
3741 }
3742}
3743
3744#[must_use]
3760pub const fn checked_wrapped_encoded_len(
3761 input_len: usize,
3762 padded: bool,
3763 wrap: LineWrap,
3764) -> Option<usize> {
3765 if wrap.line_len == 0 {
3766 return None;
3767 }
3768
3769 let Some(encoded) = checked_encoded_len(input_len, padded) else {
3770 return None;
3771 };
3772 if encoded == 0 {
3773 return Some(0);
3774 }
3775
3776 let breaks = (encoded - 1) / wrap.line_len;
3777 let Some(line_ending_bytes) = breaks.checked_mul(wrap.line_ending.byte_len()) else {
3778 return None;
3779 };
3780 encoded.checked_add(line_ending_bytes)
3781}
3782
3783#[must_use]
3794pub const fn checked_encoded_len(input_len: usize, padded: bool) -> Option<usize> {
3795 let groups = input_len / 3;
3796 if groups > usize::MAX / 4 {
3797 return None;
3798 }
3799 let full = groups * 4;
3800 let rem = input_len % 3;
3801 if rem == 0 {
3802 Some(full)
3803 } else if padded {
3804 full.checked_add(4)
3805 } else {
3806 full.checked_add(rem + 1)
3807 }
3808}
3809
3810#[must_use]
3821pub const fn decoded_capacity(encoded_len: usize) -> usize {
3822 let rem = encoded_len % 4;
3823 encoded_len / 4 * 3
3824 + if rem == 2 {
3825 1
3826 } else if rem == 3 {
3827 2
3828 } else {
3829 0
3830 }
3831}
3832
3833pub fn decoded_len(input: &[u8], padded: bool) -> Result<usize, DecodeError> {
3847 if padded {
3848 decoded_len_padded(input)
3849 } else {
3850 decoded_len_unpadded(input)
3851 }
3852}
3853
3854#[macro_export]
3886macro_rules! define_alphabet {
3887 ($(#[$meta:meta])* $vis:vis struct $name:ident = $encode:expr;) => {
3888 $(#[$meta])*
3889 #[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
3890 $vis struct $name;
3891
3892 impl $crate::Alphabet for $name {
3893 const ENCODE: [u8; 64] = *$encode;
3894
3895 #[inline]
3896 fn decode(byte: u8) -> Option<u8> {
3897 $crate::decode_alphabet_byte(byte, &Self::ENCODE)
3898 }
3899 }
3900
3901 const _: [(); 1] = [(); match $crate::validate_alphabet(
3902 &<$name as $crate::Alphabet>::ENCODE,
3903 ) {
3904 Ok(()) => 1,
3905 Err(_) => 0,
3906 }];
3907 };
3908}
3909
3910pub const fn validate_alphabet(encode: &[u8; 64]) -> Result<(), AlphabetError> {
3923 let mut index = 0;
3924 while index < encode.len() {
3925 let byte = encode[index];
3926 if !is_visible_ascii(byte) {
3927 return Err(AlphabetError::InvalidByte { index, byte });
3928 }
3929 if byte == b'=' {
3930 return Err(AlphabetError::PaddingByte { index });
3931 }
3932
3933 let mut duplicate = index + 1;
3934 while duplicate < encode.len() {
3935 if encode[duplicate] == byte {
3936 return Err(AlphabetError::DuplicateByte {
3937 first: index,
3938 second: duplicate,
3939 byte,
3940 });
3941 }
3942 duplicate += 1;
3943 }
3944
3945 index += 1;
3946 }
3947
3948 Ok(())
3949}
3950
3951#[must_use]
3977pub const fn decode_alphabet_byte(byte: u8, encode: &[u8; 64]) -> Option<u8> {
3978 let mut index = 0;
3979 let mut value = 0;
3980 while index < encode.len() {
3981 if encode[index] == byte {
3982 return Some(value);
3983 }
3984 index += 1;
3985 value += 1;
3986 }
3987 None
3988}
3989
3990pub trait Alphabet {
3992 const ENCODE: [u8; 64];
3994
3995 #[must_use]
4006 fn encode(value: u8) -> u8 {
4007 encode_alphabet_value(value, &Self::ENCODE)
4008 }
4009
4010 fn decode(byte: u8) -> Option<u8>;
4012}
4013
4014const fn is_visible_ascii(byte: u8) -> bool {
4015 byte >= 0x21 && byte <= 0x7e
4016}
4017
4018#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
4020pub struct Standard;
4021
4022impl Alphabet for Standard {
4023 const ENCODE: [u8; 64] = *b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
4024
4025 #[inline]
4026 fn encode(value: u8) -> u8 {
4027 encode_ascii_base64(value, Self::ENCODE[62], Self::ENCODE[63])
4028 }
4029
4030 #[inline]
4031 fn decode(byte: u8) -> Option<u8> {
4032 decode_ascii_base64(byte, Self::ENCODE[62], Self::ENCODE[63])
4033 }
4034}
4035
4036#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
4038pub struct UrlSafe;
4039
4040impl Alphabet for UrlSafe {
4041 const ENCODE: [u8; 64] = *b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
4042
4043 #[inline]
4044 fn encode(value: u8) -> u8 {
4045 encode_ascii_base64(value, Self::ENCODE[62], Self::ENCODE[63])
4046 }
4047
4048 #[inline]
4049 fn decode(byte: u8) -> Option<u8> {
4050 decode_ascii_base64(byte, Self::ENCODE[62], Self::ENCODE[63])
4051 }
4052}
4053
4054#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
4060pub struct Bcrypt;
4061
4062impl Alphabet for Bcrypt {
4063 const ENCODE: [u8; 64] = *b"./ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
4064
4065 #[inline]
4066 fn decode(byte: u8) -> Option<u8> {
4067 decode_alphabet_byte(byte, &Self::ENCODE)
4068 }
4069}
4070
4071#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
4076pub struct Crypt;
4077
4078impl Alphabet for Crypt {
4079 const ENCODE: [u8; 64] = *b"./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
4080
4081 #[inline]
4082 fn decode(byte: u8) -> Option<u8> {
4083 decode_alphabet_byte(byte, &Self::ENCODE)
4084 }
4085}
4086
4087#[inline]
4088const fn encode_base64_value<A: Alphabet>(value: u8) -> u8 {
4089 encode_alphabet_value(value, &A::ENCODE)
4090}
4091
4092#[inline]
4093fn encode_base64_value_runtime<A: Alphabet>(value: u8) -> u8 {
4094 A::encode(value)
4095}
4096
4097#[inline]
4098const fn encode_alphabet_value(value: u8, encode: &[u8; 64]) -> u8 {
4099 let mut output = 0;
4100 let mut index = 0;
4101 let mut candidate = 0;
4102 while index < encode.len() {
4103 output |= encode[index] & ct_mask_eq_u8(value, candidate);
4104 index += 1;
4105 candidate += 1;
4106 }
4107 output
4108}
4109
4110#[inline]
4111const fn encode_ascii_base64(value: u8, value_62_byte: u8, value_63_byte: u8) -> u8 {
4112 let upper = ct_mask_lt_u8(value, 26);
4113 let lower = ct_mask_lt_u8(value.wrapping_sub(26), 26);
4114 let digit = ct_mask_lt_u8(value.wrapping_sub(52), 10);
4115 let value_62 = ct_mask_eq_u8(value, 0x3e);
4116 let value_63 = ct_mask_eq_u8(value, 0x3f);
4117
4118 (value.wrapping_add(b'A') & upper)
4119 | (value.wrapping_sub(26).wrapping_add(b'a') & lower)
4120 | (value.wrapping_sub(52).wrapping_add(b'0') & digit)
4121 | (value_62_byte & value_62)
4122 | (value_63_byte & value_63)
4123}
4124
4125#[inline]
4126fn decode_ascii_base64(byte: u8, value_62_byte: u8, value_63_byte: u8) -> Option<u8> {
4127 let upper = ct_mask_lt_u8(byte.wrapping_sub(b'A'), 26);
4128 let lower = ct_mask_lt_u8(byte.wrapping_sub(b'a'), 26);
4129 let digit = ct_mask_lt_u8(byte.wrapping_sub(b'0'), 10);
4130 let value_62 = ct_mask_eq_u8(byte, value_62_byte);
4131 let value_63 = ct_mask_eq_u8(byte, value_63_byte);
4132 let valid = upper | lower | digit | value_62 | value_63;
4133
4134 let decoded = (byte.wrapping_sub(b'A') & upper)
4135 | (byte.wrapping_sub(b'a').wrapping_add(26) & lower)
4136 | (byte.wrapping_sub(b'0').wrapping_add(52) & digit)
4137 | (0x3e & value_62)
4138 | (0x3f & value_63);
4139
4140 if valid == 0 { None } else { Some(decoded) }
4141}
4142
4143#[inline]
4144const fn ct_mask_bit(bit: u8) -> u8 {
4145 0u8.wrapping_sub(bit & 1)
4146}
4147
4148#[inline]
4149const fn ct_mask_nonzero_u8(value: u8) -> u8 {
4150 let wide = value as u16;
4151 let negative = 0u16.wrapping_sub(wide);
4152 let nonzero = ((wide | negative) >> 8) as u8;
4153 ct_mask_bit(nonzero)
4154}
4155
4156#[inline]
4157const fn ct_mask_eq_u8(left: u8, right: u8) -> u8 {
4158 !ct_mask_nonzero_u8(left ^ right)
4159}
4160
4161#[inline]
4162const fn ct_mask_lt_u8(left: u8, right: u8) -> u8 {
4163 let diff = (left as u16).wrapping_sub(right as u16);
4164 ct_mask_bit((diff >> 8) as u8)
4165}
4166
4167fn constant_time_eq_public_len(left: &[u8], right: &[u8]) -> bool {
4168 if left.len() != right.len() {
4169 return false;
4170 }
4171
4172 let diff = left.iter().zip(right).fold(0u8, |diff, (left, right)| {
4173 diff | core::hint::black_box(*left ^ *right)
4174 });
4175 diff == 0
4176}
4177
4178mod backend {
4179 use super::{
4180 Alphabet, DecodeError, EncodeError, checked_encoded_len, decode_padded, decode_unpadded,
4181 encode_base64_value_runtime,
4182 };
4183
4184 pub(super) fn encode_slice<A, const PAD: bool>(
4185 input: &[u8],
4186 output: &mut [u8],
4187 ) -> Result<usize, EncodeError>
4188 where
4189 A: Alphabet,
4190 {
4191 #[cfg(feature = "simd")]
4192 match super::simd::active_backend() {
4193 super::simd::ActiveBackend::Scalar => {}
4194 }
4195
4196 scalar_encode_slice::<A, PAD>(input, output)
4197 }
4198
4199 pub(super) fn decode_slice<A, const PAD: bool>(
4200 input: &[u8],
4201 output: &mut [u8],
4202 ) -> Result<usize, DecodeError>
4203 where
4204 A: Alphabet,
4205 {
4206 #[cfg(feature = "simd")]
4207 match super::simd::active_backend() {
4208 super::simd::ActiveBackend::Scalar => {}
4209 }
4210
4211 scalar_decode_slice::<A, PAD>(input, output)
4212 }
4213
4214 #[cfg(test)]
4215 pub(super) fn scalar_reference_encode_slice<A, const PAD: bool>(
4216 input: &[u8],
4217 output: &mut [u8],
4218 ) -> Result<usize, EncodeError>
4219 where
4220 A: Alphabet,
4221 {
4222 scalar_encode_slice::<A, PAD>(input, output)
4223 }
4224
4225 #[cfg(test)]
4226 pub(super) fn scalar_reference_decode_slice<A, const PAD: bool>(
4227 input: &[u8],
4228 output: &mut [u8],
4229 ) -> Result<usize, DecodeError>
4230 where
4231 A: Alphabet,
4232 {
4233 scalar_decode_slice::<A, PAD>(input, output)
4234 }
4235
4236 fn scalar_encode_slice<A, const PAD: bool>(
4237 input: &[u8],
4238 output: &mut [u8],
4239 ) -> Result<usize, EncodeError>
4240 where
4241 A: Alphabet,
4242 {
4243 let required = checked_encoded_len(input.len(), PAD).ok_or(EncodeError::LengthOverflow)?;
4244 if output.len() < required {
4245 return Err(EncodeError::OutputTooSmall {
4246 required,
4247 available: output.len(),
4248 });
4249 }
4250
4251 let mut read = 0;
4252 let mut write = 0;
4253 while read + 3 <= input.len() {
4254 let b0 = input[read];
4255 let b1 = input[read + 1];
4256 let b2 = input[read + 2];
4257
4258 output[write] = encode_base64_value_runtime::<A>(b0 >> 2);
4259 output[write + 1] =
4260 encode_base64_value_runtime::<A>(((b0 & 0b0000_0011) << 4) | (b1 >> 4));
4261 output[write + 2] =
4262 encode_base64_value_runtime::<A>(((b1 & 0b0000_1111) << 2) | (b2 >> 6));
4263 output[write + 3] = encode_base64_value_runtime::<A>(b2 & 0b0011_1111);
4264
4265 read += 3;
4266 write += 4;
4267 }
4268
4269 match input.len() - read {
4270 0 => {}
4271 1 => {
4272 let b0 = input[read];
4273 output[write] = encode_base64_value_runtime::<A>(b0 >> 2);
4274 output[write + 1] = encode_base64_value_runtime::<A>((b0 & 0b0000_0011) << 4);
4275 write += 2;
4276 if PAD {
4277 output[write] = b'=';
4278 output[write + 1] = b'=';
4279 write += 2;
4280 }
4281 }
4282 2 => {
4283 let b0 = input[read];
4284 let b1 = input[read + 1];
4285 output[write] = encode_base64_value_runtime::<A>(b0 >> 2);
4286 output[write + 1] =
4287 encode_base64_value_runtime::<A>(((b0 & 0b0000_0011) << 4) | (b1 >> 4));
4288 output[write + 2] = encode_base64_value_runtime::<A>((b1 & 0b0000_1111) << 2);
4289 write += 3;
4290 if PAD {
4291 output[write] = b'=';
4292 write += 1;
4293 }
4294 }
4295 _ => unreachable!(),
4296 }
4297
4298 Ok(write)
4299 }
4300
4301 fn scalar_decode_slice<A, const PAD: bool>(
4302 input: &[u8],
4303 output: &mut [u8],
4304 ) -> Result<usize, DecodeError>
4305 where
4306 A: Alphabet,
4307 {
4308 if input.is_empty() {
4309 return Ok(0);
4310 }
4311
4312 if PAD {
4313 decode_padded::<A>(input, output)
4314 } else {
4315 decode_unpadded::<A>(input, output)
4316 }
4317 }
4318}
4319
4320pub struct Engine<A, const PAD: bool> {
4322 alphabet: core::marker::PhantomData<A>,
4323}
4324
4325impl<A, const PAD: bool> Clone for Engine<A, PAD> {
4326 fn clone(&self) -> Self {
4327 *self
4328 }
4329}
4330
4331impl<A, const PAD: bool> Copy for Engine<A, PAD> {}
4332
4333impl<A, const PAD: bool> core::fmt::Debug for Engine<A, PAD> {
4334 fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
4335 formatter
4336 .debug_struct("Engine")
4337 .field("padded", &PAD)
4338 .finish()
4339 }
4340}
4341
4342impl<A, const PAD: bool> core::fmt::Display for Engine<A, PAD> {
4343 fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
4344 write!(formatter, "padded={PAD}")
4345 }
4346}
4347
4348impl<A, const PAD: bool> Default for Engine<A, PAD> {
4349 fn default() -> Self {
4350 Self {
4351 alphabet: core::marker::PhantomData,
4352 }
4353 }
4354}
4355
4356impl<A, const PAD: bool> Eq for Engine<A, PAD> {}
4357
4358impl<A, const PAD: bool> PartialEq for Engine<A, PAD> {
4359 fn eq(&self, _other: &Self) -> bool {
4360 true
4361 }
4362}
4363
4364impl<A, const PAD: bool> Engine<A, PAD>
4365where
4366 A: Alphabet,
4367{
4368 #[must_use]
4370 pub const fn new() -> Self {
4371 Self {
4372 alphabet: core::marker::PhantomData,
4373 }
4374 }
4375
4376 #[must_use]
4378 pub const fn is_padded(&self) -> bool {
4379 PAD
4380 }
4381
4382 #[must_use]
4387 pub const fn profile(&self) -> Profile<A, PAD> {
4388 Profile::new(*self, None)
4389 }
4390
4391 #[must_use]
4397 pub const fn ct_decoder(&self) -> ct::CtEngine<A, PAD> {
4398 ct::CtEngine::new()
4399 }
4400
4401 #[cfg(feature = "stream")]
4415 #[must_use]
4416 pub fn encoder_writer<W>(&self, inner: W) -> stream::Encoder<W, A, PAD> {
4417 stream::Encoder::new(inner, *self)
4418 }
4419
4420 #[cfg(feature = "stream")]
4434 #[must_use]
4435 pub fn decoder_writer<W>(&self, inner: W) -> stream::Decoder<W, A, PAD> {
4436 stream::Decoder::new(inner, *self)
4437 }
4438
4439 #[cfg(feature = "stream")]
4454 #[must_use]
4455 pub fn encoder_reader<R>(&self, inner: R) -> stream::EncoderReader<R, A, PAD> {
4456 stream::EncoderReader::new(inner, *self)
4457 }
4458
4459 #[cfg(feature = "stream")]
4474 #[must_use]
4475 pub fn decoder_reader<R>(&self, inner: R) -> stream::DecoderReader<R, A, PAD> {
4476 stream::DecoderReader::new(inner, *self)
4477 }
4478
4479 pub const fn encoded_len(&self, input_len: usize) -> Result<usize, EncodeError> {
4481 encoded_len(input_len, PAD)
4482 }
4483
4484 #[must_use]
4486 pub const fn checked_encoded_len(&self, input_len: usize) -> Option<usize> {
4487 checked_encoded_len(input_len, PAD)
4488 }
4489
4490 pub const fn wrapped_encoded_len(
4495 &self,
4496 input_len: usize,
4497 wrap: LineWrap,
4498 ) -> Result<usize, EncodeError> {
4499 wrapped_encoded_len(input_len, PAD, wrap)
4500 }
4501
4502 #[must_use]
4505 pub const fn checked_wrapped_encoded_len(
4506 &self,
4507 input_len: usize,
4508 wrap: LineWrap,
4509 ) -> Option<usize> {
4510 checked_wrapped_encoded_len(input_len, PAD, wrap)
4511 }
4512
4513 pub fn decoded_len(&self, input: &[u8]) -> Result<usize, DecodeError> {
4518 decoded_len(input, PAD)
4519 }
4520
4521 pub fn decoded_len_legacy(&self, input: &[u8]) -> Result<usize, DecodeError> {
4527 validate_legacy_decode::<A, PAD>(input)
4528 }
4529
4530 pub fn decoded_len_wrapped(&self, input: &[u8], wrap: LineWrap) -> Result<usize, DecodeError> {
4537 validate_wrapped_decode::<A, PAD>(input, wrap)
4538 }
4539
4540 pub fn validate_result(&self, input: &[u8]) -> Result<(), DecodeError> {
4555 validate_decode::<A, PAD>(input).map(|_| ())
4556 }
4557
4558 #[must_use]
4571 pub fn validate(&self, input: &[u8]) -> bool {
4572 self.validate_result(input).is_ok()
4573 }
4574
4575 pub fn validate_legacy_result(&self, input: &[u8]) -> Result<(), DecodeError> {
4590 validate_legacy_decode::<A, PAD>(input).map(|_| ())
4591 }
4592
4593 #[must_use]
4607 pub fn validate_legacy(&self, input: &[u8]) -> bool {
4608 self.validate_legacy_result(input).is_ok()
4609 }
4610
4611 pub fn validate_wrapped_result(&self, input: &[u8], wrap: LineWrap) -> Result<(), DecodeError> {
4627 validate_wrapped_decode::<A, PAD>(input, wrap).map(|_| ())
4628 }
4629
4630 #[must_use]
4644 pub fn validate_wrapped(&self, input: &[u8], wrap: LineWrap) -> bool {
4645 self.validate_wrapped_result(input, wrap).is_ok()
4646 }
4647
4648 #[must_use]
4680 pub const fn encode_array<const INPUT_LEN: usize, const OUTPUT_LEN: usize>(
4681 &self,
4682 input: &[u8; INPUT_LEN],
4683 ) -> [u8; OUTPUT_LEN] {
4684 let Some(required) = checked_encoded_len(INPUT_LEN, PAD) else {
4685 panic!("encoded base64 length overflows usize");
4686 };
4687 assert!(
4688 required == OUTPUT_LEN,
4689 "base64 output array has incorrect length"
4690 );
4691
4692 let mut output = [0u8; OUTPUT_LEN];
4693 let mut read = 0;
4694 let mut write = 0;
4695 while INPUT_LEN - read >= 3 {
4696 let b0 = input[read];
4697 let b1 = input[read + 1];
4698 let b2 = input[read + 2];
4699
4700 output[write] = encode_base64_value::<A>(b0 >> 2);
4701 output[write + 1] = encode_base64_value::<A>(((b0 & 0b0000_0011) << 4) | (b1 >> 4));
4702 output[write + 2] = encode_base64_value::<A>(((b1 & 0b0000_1111) << 2) | (b2 >> 6));
4703 output[write + 3] = encode_base64_value::<A>(b2 & 0b0011_1111);
4704
4705 read += 3;
4706 write += 4;
4707 }
4708
4709 match INPUT_LEN - read {
4710 0 => {}
4711 1 => {
4712 let b0 = input[read];
4713 output[write] = encode_base64_value::<A>(b0 >> 2);
4714 output[write + 1] = encode_base64_value::<A>((b0 & 0b0000_0011) << 4);
4715 write += 2;
4716 if PAD {
4717 output[write] = b'=';
4718 output[write + 1] = b'=';
4719 }
4720 }
4721 2 => {
4722 let b0 = input[read];
4723 let b1 = input[read + 1];
4724 output[write] = encode_base64_value::<A>(b0 >> 2);
4725 output[write + 1] = encode_base64_value::<A>(((b0 & 0b0000_0011) << 4) | (b1 >> 4));
4726 output[write + 2] = encode_base64_value::<A>((b1 & 0b0000_1111) << 2);
4727 if PAD {
4728 output[write + 3] = b'=';
4729 }
4730 }
4731 _ => unreachable!(),
4732 }
4733
4734 output
4735 }
4736
4737 pub fn encode_slice(&self, input: &[u8], output: &mut [u8]) -> Result<usize, EncodeError> {
4739 backend::encode_slice::<A, PAD>(input, output)
4740 }
4741
4742 pub fn encode_slice_wrapped(
4761 &self,
4762 input: &[u8],
4763 output: &mut [u8],
4764 wrap: LineWrap,
4765 ) -> Result<usize, EncodeError> {
4766 let required = self.wrapped_encoded_len(input.len(), wrap)?;
4767 if output.len() < required {
4768 return Err(EncodeError::OutputTooSmall {
4769 required,
4770 available: output.len(),
4771 });
4772 }
4773
4774 let encoded_len =
4775 checked_encoded_len(input.len(), PAD).ok_or(EncodeError::LengthOverflow)?;
4776 if encoded_len == 0 {
4777 return Ok(0);
4778 }
4779
4780 if output.len() < required.saturating_add(encoded_len) {
4781 let mut scratch = [0u8; 1024];
4782 let mut input_offset = 0;
4783 let mut output_offset = 0;
4784 let mut column = 0;
4785
4786 while input_offset < input.len() {
4787 let remaining = input.len() - input_offset;
4788 let mut take = remaining.min(768);
4789 if remaining > take {
4790 take -= take % 3;
4791 }
4792 if take == 0 {
4793 take = remaining;
4794 }
4795
4796 let encoded =
4797 self.encode_slice(&input[input_offset..input_offset + take], &mut scratch)?;
4798 write_wrapped_bytes(
4799 &scratch[..encoded],
4800 output,
4801 &mut output_offset,
4802 &mut column,
4803 wrap,
4804 );
4805 wipe_bytes(&mut scratch[..encoded]);
4806 input_offset += take;
4807 }
4808
4809 Ok(output_offset)
4810 } else {
4811 let encoded =
4812 self.encode_slice(input, &mut output[required..required + encoded_len])?;
4813 let mut output_offset = 0;
4814 let mut column = 0;
4815 let mut read = required;
4816 while read < required + encoded {
4817 let byte = output[read];
4818 write_wrapped_byte(byte, output, &mut output_offset, &mut column, wrap);
4819 read += 1;
4820 }
4821 wipe_bytes(&mut output[required..required + encoded]);
4822 Ok(output_offset)
4823 }
4824 }
4825
4826 pub fn encode_slice_wrapped_clear_tail(
4832 &self,
4833 input: &[u8],
4834 output: &mut [u8],
4835 wrap: LineWrap,
4836 ) -> Result<usize, EncodeError> {
4837 let written = match self.encode_slice_wrapped(input, output, wrap) {
4838 Ok(written) => written,
4839 Err(err) => {
4840 wipe_bytes(output);
4841 return Err(err);
4842 }
4843 };
4844 wipe_tail(output, written);
4845 Ok(written)
4846 }
4847
4848 pub fn encode_wrapped_buffer<const CAP: usize>(
4854 &self,
4855 input: &[u8],
4856 wrap: LineWrap,
4857 ) -> Result<EncodedBuffer<CAP>, EncodeError> {
4858 let mut output = EncodedBuffer::new();
4859 let written = match self.encode_slice_wrapped_clear_tail(input, &mut output.bytes, wrap) {
4860 Ok(written) => written,
4861 Err(err) => {
4862 output.clear();
4863 return Err(err);
4864 }
4865 };
4866 output.len = written;
4867 Ok(output)
4868 }
4869
4870 #[cfg(feature = "alloc")]
4872 pub fn encode_wrapped_vec(
4873 &self,
4874 input: &[u8],
4875 wrap: LineWrap,
4876 ) -> Result<alloc::vec::Vec<u8>, EncodeError> {
4877 let required = self.wrapped_encoded_len(input.len(), wrap)?;
4878 let mut output = alloc::vec![0; required];
4879 let written = self.encode_slice_wrapped(input, &mut output, wrap)?;
4880 output.truncate(written);
4881 Ok(output)
4882 }
4883
4884 #[cfg(feature = "alloc")]
4886 pub fn encode_wrapped_string(
4887 &self,
4888 input: &[u8],
4889 wrap: LineWrap,
4890 ) -> Result<alloc::string::String, EncodeError> {
4891 let output = self.encode_wrapped_vec(input, wrap)?;
4892 match alloc::string::String::from_utf8(output) {
4893 Ok(output) => Ok(output),
4894 Err(_) => unreachable!("base64 encoder produced non-UTF-8 output"),
4895 }
4896 }
4897
4898 #[cfg(feature = "alloc")]
4903 pub fn encode_wrapped_secret(
4904 &self,
4905 input: &[u8],
4906 wrap: LineWrap,
4907 ) -> Result<SecretBuffer, EncodeError> {
4908 self.encode_wrapped_vec(input, wrap)
4909 .map(SecretBuffer::from_vec)
4910 }
4911
4912 pub fn encode_slice_clear_tail(
4932 &self,
4933 input: &[u8],
4934 output: &mut [u8],
4935 ) -> Result<usize, EncodeError> {
4936 let written = match self.encode_slice(input, output) {
4937 Ok(written) => written,
4938 Err(err) => {
4939 wipe_bytes(output);
4940 return Err(err);
4941 }
4942 };
4943 wipe_tail(output, written);
4944 Ok(written)
4945 }
4946
4947 pub fn encode_buffer<const CAP: usize>(
4962 &self,
4963 input: &[u8],
4964 ) -> Result<EncodedBuffer<CAP>, EncodeError> {
4965 let mut output = EncodedBuffer::new();
4966 let written = match self.encode_slice_clear_tail(input, &mut output.bytes) {
4967 Ok(written) => written,
4968 Err(err) => {
4969 output.clear();
4970 return Err(err);
4971 }
4972 };
4973 output.len = written;
4974 Ok(output)
4975 }
4976
4977 #[cfg(feature = "alloc")]
4979 pub fn encode_vec(&self, input: &[u8]) -> Result<alloc::vec::Vec<u8>, EncodeError> {
4980 let required = checked_encoded_len(input.len(), PAD).ok_or(EncodeError::LengthOverflow)?;
4981 let mut output = alloc::vec![0; required];
4982 let written = self.encode_slice(input, &mut output)?;
4983 output.truncate(written);
4984 Ok(output)
4985 }
4986
4987 #[cfg(feature = "alloc")]
4992 pub fn encode_secret(&self, input: &[u8]) -> Result<SecretBuffer, EncodeError> {
4993 self.encode_vec(input).map(SecretBuffer::from_vec)
4994 }
4995
4996 #[cfg(feature = "alloc")]
5011 pub fn encode_string(&self, input: &[u8]) -> Result<alloc::string::String, EncodeError> {
5012 let output = self.encode_vec(input)?;
5013 match alloc::string::String::from_utf8(output) {
5014 Ok(output) => Ok(output),
5015 Err(_) => unreachable!("base64 encoder produced non-UTF-8 output"),
5016 }
5017 }
5018
5019 pub fn encode_in_place<'a>(
5036 &self,
5037 buffer: &'a mut [u8],
5038 input_len: usize,
5039 ) -> Result<&'a mut [u8], EncodeError> {
5040 if input_len > buffer.len() {
5041 return Err(EncodeError::InputTooLarge {
5042 input_len,
5043 buffer_len: buffer.len(),
5044 });
5045 }
5046
5047 let required = checked_encoded_len(input_len, PAD).ok_or(EncodeError::LengthOverflow)?;
5048 if buffer.len() < required {
5049 return Err(EncodeError::OutputTooSmall {
5050 required,
5051 available: buffer.len(),
5052 });
5053 }
5054
5055 let mut read = input_len;
5056 let mut write = required;
5057
5058 match input_len % 3 {
5059 0 => {}
5060 1 => {
5061 read -= 1;
5062 let b0 = buffer[read];
5063 if PAD {
5064 write -= 4;
5065 buffer[write] = encode_base64_value_runtime::<A>(b0 >> 2);
5066 buffer[write + 1] = encode_base64_value_runtime::<A>((b0 & 0b0000_0011) << 4);
5067 buffer[write + 2] = b'=';
5068 buffer[write + 3] = b'=';
5069 } else {
5070 write -= 2;
5071 buffer[write] = encode_base64_value_runtime::<A>(b0 >> 2);
5072 buffer[write + 1] = encode_base64_value_runtime::<A>((b0 & 0b0000_0011) << 4);
5073 }
5074 }
5075 2 => {
5076 read -= 2;
5077 let b0 = buffer[read];
5078 let b1 = buffer[read + 1];
5079 if PAD {
5080 write -= 4;
5081 buffer[write] = encode_base64_value_runtime::<A>(b0 >> 2);
5082 buffer[write + 1] =
5083 encode_base64_value_runtime::<A>(((b0 & 0b0000_0011) << 4) | (b1 >> 4));
5084 buffer[write + 2] = encode_base64_value_runtime::<A>((b1 & 0b0000_1111) << 2);
5085 buffer[write + 3] = b'=';
5086 } else {
5087 write -= 3;
5088 buffer[write] = encode_base64_value_runtime::<A>(b0 >> 2);
5089 buffer[write + 1] =
5090 encode_base64_value_runtime::<A>(((b0 & 0b0000_0011) << 4) | (b1 >> 4));
5091 buffer[write + 2] = encode_base64_value_runtime::<A>((b1 & 0b0000_1111) << 2);
5092 }
5093 }
5094 _ => unreachable!(),
5095 }
5096
5097 while read > 0 {
5098 read -= 3;
5099 write -= 4;
5100 let b0 = buffer[read];
5101 let b1 = buffer[read + 1];
5102 let b2 = buffer[read + 2];
5103
5104 buffer[write] = encode_base64_value_runtime::<A>(b0 >> 2);
5105 buffer[write + 1] =
5106 encode_base64_value_runtime::<A>(((b0 & 0b0000_0011) << 4) | (b1 >> 4));
5107 buffer[write + 2] =
5108 encode_base64_value_runtime::<A>(((b1 & 0b0000_1111) << 2) | (b2 >> 6));
5109 buffer[write + 3] = encode_base64_value_runtime::<A>(b2 & 0b0011_1111);
5110 }
5111
5112 debug_assert_eq!(write, 0);
5113 Ok(&mut buffer[..required])
5114 }
5115
5116 pub fn encode_in_place_clear_tail<'a>(
5134 &self,
5135 buffer: &'a mut [u8],
5136 input_len: usize,
5137 ) -> Result<&'a mut [u8], EncodeError> {
5138 let len = match self.encode_in_place(buffer, input_len) {
5139 Ok(encoded) => encoded.len(),
5140 Err(err) => {
5141 wipe_bytes(buffer);
5142 return Err(err);
5143 }
5144 };
5145 wipe_tail(buffer, len);
5146 Ok(&mut buffer[..len])
5147 }
5148
5149 pub fn decode_slice(&self, input: &[u8], output: &mut [u8]) -> Result<usize, DecodeError> {
5154 backend::decode_slice::<A, PAD>(input, output)
5155 }
5156
5157 pub fn decode_slice_clear_tail(
5177 &self,
5178 input: &[u8],
5179 output: &mut [u8],
5180 ) -> Result<usize, DecodeError> {
5181 let written = match self.decode_slice(input, output) {
5182 Ok(written) => written,
5183 Err(err) => {
5184 wipe_bytes(output);
5185 return Err(err);
5186 }
5187 };
5188 wipe_tail(output, written);
5189 Ok(written)
5190 }
5191
5192 pub fn decode_buffer<const CAP: usize>(
5207 &self,
5208 input: &[u8],
5209 ) -> Result<DecodedBuffer<CAP>, DecodeError> {
5210 let mut output = DecodedBuffer::new();
5211 let written = match self.decode_slice_clear_tail(input, &mut output.bytes) {
5212 Ok(written) => written,
5213 Err(err) => {
5214 output.clear();
5215 return Err(err);
5216 }
5217 };
5218 output.len = written;
5219 Ok(output)
5220 }
5221
5222 pub fn decode_slice_legacy(
5228 &self,
5229 input: &[u8],
5230 output: &mut [u8],
5231 ) -> Result<usize, DecodeError> {
5232 let required = validate_legacy_decode::<A, PAD>(input)?;
5233 if output.len() < required {
5234 return Err(DecodeError::OutputTooSmall {
5235 required,
5236 available: output.len(),
5237 });
5238 }
5239 decode_legacy_to_slice::<A, PAD>(input, output)
5240 }
5241
5242 pub fn decode_slice_legacy_clear_tail(
5262 &self,
5263 input: &[u8],
5264 output: &mut [u8],
5265 ) -> Result<usize, DecodeError> {
5266 let written = match self.decode_slice_legacy(input, output) {
5267 Ok(written) => written,
5268 Err(err) => {
5269 wipe_bytes(output);
5270 return Err(err);
5271 }
5272 };
5273 wipe_tail(output, written);
5274 Ok(written)
5275 }
5276
5277 pub fn decode_buffer_legacy<const CAP: usize>(
5285 &self,
5286 input: &[u8],
5287 ) -> Result<DecodedBuffer<CAP>, DecodeError> {
5288 let mut output = DecodedBuffer::new();
5289 let written = match self.decode_slice_legacy_clear_tail(input, &mut output.bytes) {
5290 Ok(written) => written,
5291 Err(err) => {
5292 output.clear();
5293 return Err(err);
5294 }
5295 };
5296 output.len = written;
5297 Ok(output)
5298 }
5299
5300 pub fn decode_slice_wrapped(
5307 &self,
5308 input: &[u8],
5309 output: &mut [u8],
5310 wrap: LineWrap,
5311 ) -> Result<usize, DecodeError> {
5312 let required = validate_wrapped_decode::<A, PAD>(input, wrap)?;
5313 if output.len() < required {
5314 return Err(DecodeError::OutputTooSmall {
5315 required,
5316 available: output.len(),
5317 });
5318 }
5319 decode_wrapped_to_slice::<A, PAD>(input, output, wrap)
5320 }
5321
5322 pub fn decode_slice_wrapped_clear_tail(
5328 &self,
5329 input: &[u8],
5330 output: &mut [u8],
5331 wrap: LineWrap,
5332 ) -> Result<usize, DecodeError> {
5333 let written = match self.decode_slice_wrapped(input, output, wrap) {
5334 Ok(written) => written,
5335 Err(err) => {
5336 wipe_bytes(output);
5337 return Err(err);
5338 }
5339 };
5340 wipe_tail(output, written);
5341 Ok(written)
5342 }
5343
5344 pub fn decode_wrapped_buffer<const CAP: usize>(
5353 &self,
5354 input: &[u8],
5355 wrap: LineWrap,
5356 ) -> Result<DecodedBuffer<CAP>, DecodeError> {
5357 let mut output = DecodedBuffer::new();
5358 let written = match self.decode_slice_wrapped_clear_tail(input, &mut output.bytes, wrap) {
5359 Ok(written) => written,
5360 Err(err) => {
5361 output.clear();
5362 return Err(err);
5363 }
5364 };
5365 output.len = written;
5366 Ok(output)
5367 }
5368
5369 #[cfg(feature = "alloc")]
5373 pub fn decode_vec(&self, input: &[u8]) -> Result<alloc::vec::Vec<u8>, DecodeError> {
5374 let required = validate_decode::<A, PAD>(input)?;
5375 let mut output = alloc::vec![0; required];
5376 let written = match self.decode_slice(input, &mut output) {
5377 Ok(written) => written,
5378 Err(err) => {
5379 wipe_bytes(&mut output);
5380 return Err(err);
5381 }
5382 };
5383 output.truncate(written);
5384 Ok(output)
5385 }
5386
5387 #[cfg(feature = "alloc")]
5392 pub fn decode_secret(&self, input: &[u8]) -> Result<SecretBuffer, DecodeError> {
5393 self.decode_vec(input).map(SecretBuffer::from_vec)
5394 }
5395
5396 #[cfg(feature = "alloc")]
5399 pub fn decode_vec_legacy(&self, input: &[u8]) -> Result<alloc::vec::Vec<u8>, DecodeError> {
5400 let required = validate_legacy_decode::<A, PAD>(input)?;
5401 let mut output = alloc::vec![0; required];
5402 let written = match self.decode_slice_legacy(input, &mut output) {
5403 Ok(written) => written,
5404 Err(err) => {
5405 wipe_bytes(&mut output);
5406 return Err(err);
5407 }
5408 };
5409 output.truncate(written);
5410 Ok(output)
5411 }
5412
5413 #[cfg(feature = "alloc")]
5420 pub fn decode_secret_legacy(&self, input: &[u8]) -> Result<SecretBuffer, DecodeError> {
5421 self.decode_vec_legacy(input).map(SecretBuffer::from_vec)
5422 }
5423
5424 #[cfg(feature = "alloc")]
5426 pub fn decode_wrapped_vec(
5427 &self,
5428 input: &[u8],
5429 wrap: LineWrap,
5430 ) -> Result<alloc::vec::Vec<u8>, DecodeError> {
5431 let required = validate_wrapped_decode::<A, PAD>(input, wrap)?;
5432 let mut output = alloc::vec![0; required];
5433 let written = match self.decode_slice_wrapped(input, &mut output, wrap) {
5434 Ok(written) => written,
5435 Err(err) => {
5436 wipe_bytes(&mut output);
5437 return Err(err);
5438 }
5439 };
5440 output.truncate(written);
5441 Ok(output)
5442 }
5443
5444 #[cfg(feature = "alloc")]
5451 pub fn decode_wrapped_secret(
5452 &self,
5453 input: &[u8],
5454 wrap: LineWrap,
5455 ) -> Result<SecretBuffer, DecodeError> {
5456 self.decode_wrapped_vec(input, wrap)
5457 .map(SecretBuffer::from_vec)
5458 }
5459
5460 pub fn decode_in_place_wrapped<'a>(
5480 &self,
5481 buffer: &'a mut [u8],
5482 wrap: LineWrap,
5483 ) -> Result<&'a mut [u8], DecodeError> {
5484 let _required = validate_wrapped_decode::<A, PAD>(buffer, wrap)?;
5485 let compacted = compact_wrapped_input(buffer, wrap)?;
5486 let len = Self::decode_slice_to_start(&mut buffer[..compacted])?;
5487 Ok(&mut buffer[..len])
5488 }
5489
5490 pub fn decode_in_place_wrapped_clear_tail<'a>(
5511 &self,
5512 buffer: &'a mut [u8],
5513 wrap: LineWrap,
5514 ) -> Result<&'a mut [u8], DecodeError> {
5515 if let Err(err) = validate_wrapped_decode::<A, PAD>(buffer, wrap) {
5516 wipe_bytes(buffer);
5517 return Err(err);
5518 }
5519
5520 let compacted = match compact_wrapped_input(buffer, wrap) {
5521 Ok(compacted) => compacted,
5522 Err(err) => {
5523 wipe_bytes(buffer);
5524 return Err(err);
5525 }
5526 };
5527
5528 let len = match Self::decode_slice_to_start(&mut buffer[..compacted]) {
5529 Ok(len) => len,
5530 Err(err) => {
5531 wipe_bytes(buffer);
5532 return Err(err);
5533 }
5534 };
5535 wipe_tail(buffer, len);
5536 Ok(&mut buffer[..len])
5537 }
5538
5539 pub fn decode_in_place<'a>(&self, buffer: &'a mut [u8]) -> Result<&'a mut [u8], DecodeError> {
5551 let len = Self::decode_slice_to_start(buffer)?;
5552 Ok(&mut buffer[..len])
5553 }
5554
5555 pub fn decode_in_place_clear_tail<'a>(
5572 &self,
5573 buffer: &'a mut [u8],
5574 ) -> Result<&'a mut [u8], DecodeError> {
5575 let len = match Self::decode_slice_to_start(buffer) {
5576 Ok(len) => len,
5577 Err(err) => {
5578 wipe_bytes(buffer);
5579 return Err(err);
5580 }
5581 };
5582 wipe_tail(buffer, len);
5583 Ok(&mut buffer[..len])
5584 }
5585
5586 pub fn decode_in_place_legacy<'a>(
5591 &self,
5592 buffer: &'a mut [u8],
5593 ) -> Result<&'a mut [u8], DecodeError> {
5594 let _required = validate_legacy_decode::<A, PAD>(buffer)?;
5595 let mut write = 0;
5596 let mut read = 0;
5597 while read < buffer.len() {
5598 let byte = buffer[read];
5599 if !is_legacy_whitespace(byte) {
5600 buffer[write] = byte;
5601 write += 1;
5602 }
5603 read += 1;
5604 }
5605 let len = Self::decode_slice_to_start(&mut buffer[..write])?;
5606 Ok(&mut buffer[..len])
5607 }
5608
5609 pub fn decode_in_place_legacy_clear_tail<'a>(
5615 &self,
5616 buffer: &'a mut [u8],
5617 ) -> Result<&'a mut [u8], DecodeError> {
5618 if let Err(err) = validate_legacy_decode::<A, PAD>(buffer) {
5619 wipe_bytes(buffer);
5620 return Err(err);
5621 }
5622
5623 let mut write = 0;
5624 let mut read = 0;
5625 while read < buffer.len() {
5626 let byte = buffer[read];
5627 if !is_legacy_whitespace(byte) {
5628 buffer[write] = byte;
5629 write += 1;
5630 }
5631 read += 1;
5632 }
5633
5634 let len = match Self::decode_slice_to_start(&mut buffer[..write]) {
5635 Ok(len) => len,
5636 Err(err) => {
5637 wipe_bytes(buffer);
5638 return Err(err);
5639 }
5640 };
5641 wipe_tail(buffer, len);
5642 Ok(&mut buffer[..len])
5643 }
5644
5645 fn decode_slice_to_start(buffer: &mut [u8]) -> Result<usize, DecodeError> {
5646 let _required = validate_decode::<A, PAD>(buffer)?;
5647 let input_len = buffer.len();
5648 let mut read = 0;
5649 let mut write = 0;
5650 while read + 4 <= input_len {
5651 let chunk = read_quad(buffer, read)?;
5652 let available = buffer.len();
5653 let output_tail = buffer.get_mut(write..).ok_or(DecodeError::OutputTooSmall {
5654 required: write,
5655 available,
5656 })?;
5657 let written = decode_chunk::<A, PAD>(chunk, output_tail)
5658 .map_err(|err| err.with_index_offset(read))?;
5659 read += 4;
5660 write += written;
5661 if written < 3 {
5662 if read != input_len {
5663 return Err(DecodeError::InvalidPadding { index: read - 4 });
5664 }
5665 return Ok(write);
5666 }
5667 }
5668
5669 let rem = input_len - read;
5670 if rem == 0 {
5671 return Ok(write);
5672 }
5673 if PAD {
5674 return Err(DecodeError::InvalidLength);
5675 }
5676 let mut tail = [0u8; 3];
5677 tail[..rem].copy_from_slice(&buffer[read..input_len]);
5678 decode_tail_unpadded::<A>(&tail[..rem], &mut buffer[write..])
5679 .map_err(|err| err.with_index_offset(read))
5680 .map(|n| write + n)
5681 }
5682}
5683
5684fn write_wrapped_bytes(
5685 input: &[u8],
5686 output: &mut [u8],
5687 output_offset: &mut usize,
5688 column: &mut usize,
5689 wrap: LineWrap,
5690) {
5691 for byte in input {
5692 write_wrapped_byte(*byte, output, output_offset, column, wrap);
5693 }
5694}
5695
5696fn write_wrapped_byte(
5697 byte: u8,
5698 output: &mut [u8],
5699 output_offset: &mut usize,
5700 column: &mut usize,
5701 wrap: LineWrap,
5702) {
5703 if *column == wrap.line_len {
5704 let line_ending = wrap.line_ending.as_bytes();
5705 let mut index = 0;
5706 while index < line_ending.len() {
5707 output[*output_offset] = line_ending[index];
5708 *output_offset += 1;
5709 index += 1;
5710 }
5711 *column = 0;
5712 }
5713
5714 output[*output_offset] = byte;
5715 *output_offset += 1;
5716 *column += 1;
5717}
5718
5719#[derive(Clone, Copy, Debug, Eq, PartialEq)]
5721pub enum EncodeError {
5722 LengthOverflow,
5724 InvalidLineWrap {
5726 line_len: usize,
5728 },
5729 InputTooLarge {
5731 input_len: usize,
5733 buffer_len: usize,
5735 },
5736 OutputTooSmall {
5738 required: usize,
5740 available: usize,
5742 },
5743}
5744
5745impl core::fmt::Display for EncodeError {
5746 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
5747 match self {
5748 Self::LengthOverflow => f.write_str("base64 output length overflows usize"),
5749 Self::InvalidLineWrap { line_len } => {
5750 write!(f, "base64 line wrap length {line_len} is invalid")
5751 }
5752 Self::InputTooLarge {
5753 input_len,
5754 buffer_len,
5755 } => write!(
5756 f,
5757 "base64 input length {input_len} exceeds buffer length {buffer_len}"
5758 ),
5759 Self::OutputTooSmall {
5760 required,
5761 available,
5762 } => write!(
5763 f,
5764 "base64 output buffer too small: required {required}, available {available}"
5765 ),
5766 }
5767 }
5768}
5769
5770#[cfg(feature = "std")]
5771impl std::error::Error for EncodeError {}
5772
5773#[derive(Clone, Copy, Debug, Eq, PartialEq)]
5775pub enum AlphabetError {
5776 InvalidByte {
5778 index: usize,
5780 byte: u8,
5782 },
5783 PaddingByte {
5785 index: usize,
5787 },
5788 DuplicateByte {
5790 first: usize,
5792 second: usize,
5794 byte: u8,
5796 },
5797}
5798
5799impl core::fmt::Display for AlphabetError {
5800 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
5801 match self {
5802 Self::InvalidByte { index, byte } => {
5803 write!(
5804 f,
5805 "invalid base64 alphabet byte 0x{byte:02x} at index {index}"
5806 )
5807 }
5808 Self::PaddingByte { index } => {
5809 write!(f, "base64 alphabet contains padding byte at index {index}")
5810 }
5811 Self::DuplicateByte {
5812 first,
5813 second,
5814 byte,
5815 } => write!(
5816 f,
5817 "base64 alphabet byte 0x{byte:02x} is duplicated at indexes {first} and {second}"
5818 ),
5819 }
5820 }
5821}
5822
5823#[cfg(feature = "std")]
5824impl std::error::Error for AlphabetError {}
5825
5826#[derive(Clone, Copy, Debug, Eq, PartialEq)]
5828pub enum DecodeError {
5829 InvalidInput,
5832 InvalidLength,
5834 InvalidByte {
5836 index: usize,
5838 byte: u8,
5840 },
5841 InvalidPadding {
5843 index: usize,
5845 },
5846 InvalidLineWrap {
5848 index: usize,
5850 },
5851 OutputTooSmall {
5853 required: usize,
5855 available: usize,
5857 },
5858}
5859
5860impl core::fmt::Display for DecodeError {
5861 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
5862 match self {
5863 Self::InvalidInput => f.write_str("malformed base64 input"),
5864 Self::InvalidLength => f.write_str("invalid base64 input length"),
5865 Self::InvalidByte { index, byte } => {
5866 write!(f, "invalid base64 byte 0x{byte:02x} at index {index}")
5867 }
5868 Self::InvalidPadding { index } => write!(f, "invalid base64 padding at index {index}"),
5869 Self::InvalidLineWrap { index } => {
5870 write!(f, "invalid base64 line wrapping at index {index}")
5871 }
5872 Self::OutputTooSmall {
5873 required,
5874 available,
5875 } => write!(
5876 f,
5877 "base64 decode output buffer too small: required {required}, available {available}"
5878 ),
5879 }
5880 }
5881}
5882
5883impl DecodeError {
5884 fn with_index_offset(self, offset: usize) -> Self {
5885 match self {
5886 Self::InvalidByte { index, byte } => Self::InvalidByte {
5887 index: index + offset,
5888 byte,
5889 },
5890 Self::InvalidPadding { index } => Self::InvalidPadding {
5891 index: index + offset,
5892 },
5893 Self::InvalidLineWrap { index } => Self::InvalidLineWrap {
5894 index: index + offset,
5895 },
5896 Self::InvalidInput | Self::InvalidLength | Self::OutputTooSmall { .. } => self,
5897 }
5898 }
5899}
5900
5901#[cfg(feature = "std")]
5902impl std::error::Error for DecodeError {}
5903
5904fn validate_legacy_decode<A: Alphabet, const PAD: bool>(
5905 input: &[u8],
5906) -> Result<usize, DecodeError> {
5907 let mut chunk = [0u8; 4];
5908 let mut indexes = [0usize; 4];
5909 let mut chunk_len = 0;
5910 let mut required = 0;
5911 let mut terminal_seen = false;
5912
5913 for (index, byte) in input.iter().copied().enumerate() {
5914 if is_legacy_whitespace(byte) {
5915 continue;
5916 }
5917 if terminal_seen {
5918 return Err(DecodeError::InvalidPadding { index });
5919 }
5920
5921 chunk[chunk_len] = byte;
5922 indexes[chunk_len] = index;
5923 chunk_len += 1;
5924
5925 if chunk_len == 4 {
5926 let written =
5927 validate_chunk::<A, PAD>(chunk).map_err(|err| map_chunk_error(err, &indexes))?;
5928 required += written;
5929 terminal_seen = written < 3;
5930 chunk_len = 0;
5931 }
5932 }
5933
5934 if chunk_len == 0 {
5935 return Ok(required);
5936 }
5937 if PAD {
5938 return Err(DecodeError::InvalidLength);
5939 }
5940
5941 validate_tail_unpadded::<A>(&chunk[..chunk_len])
5942 .map_err(|err| map_partial_chunk_error(err, &indexes, chunk_len))?;
5943 Ok(required + decoded_capacity(chunk_len))
5944}
5945
5946fn decode_legacy_to_slice<A: Alphabet, const PAD: bool>(
5947 input: &[u8],
5948 output: &mut [u8],
5949) -> Result<usize, DecodeError> {
5950 let mut chunk = [0u8; 4];
5951 let mut indexes = [0usize; 4];
5952 let mut chunk_len = 0;
5953 let mut write = 0;
5954 let mut terminal_seen = false;
5955
5956 for (index, byte) in input.iter().copied().enumerate() {
5957 if is_legacy_whitespace(byte) {
5958 continue;
5959 }
5960 if terminal_seen {
5961 return Err(DecodeError::InvalidPadding { index });
5962 }
5963
5964 chunk[chunk_len] = byte;
5965 indexes[chunk_len] = index;
5966 chunk_len += 1;
5967
5968 if chunk_len == 4 {
5969 let available = output.len();
5970 let output_tail = output.get_mut(write..).ok_or(DecodeError::OutputTooSmall {
5971 required: write,
5972 available,
5973 })?;
5974 let written = decode_chunk::<A, PAD>(chunk, output_tail)
5975 .map_err(|err| map_chunk_error(err, &indexes))?;
5976 write += written;
5977 terminal_seen = written < 3;
5978 chunk_len = 0;
5979 }
5980 }
5981
5982 if chunk_len == 0 {
5983 return Ok(write);
5984 }
5985 if PAD {
5986 return Err(DecodeError::InvalidLength);
5987 }
5988
5989 decode_tail_unpadded::<A>(&chunk[..chunk_len], &mut output[write..])
5990 .map_err(|err| map_partial_chunk_error(err, &indexes, chunk_len))
5991 .map(|n| write + n)
5992}
5993
5994struct WrappedBytes<'a> {
5995 input: &'a [u8],
5996 wrap: LineWrap,
5997 index: usize,
5998 line_len: usize,
5999}
6000
6001impl<'a> WrappedBytes<'a> {
6002 const fn new(input: &'a [u8], wrap: LineWrap) -> Result<Self, DecodeError> {
6003 if wrap.line_len == 0 {
6004 return Err(DecodeError::InvalidLineWrap { index: 0 });
6005 }
6006 Ok(Self {
6007 input,
6008 wrap,
6009 index: 0,
6010 line_len: 0,
6011 })
6012 }
6013
6014 fn next_byte(&mut self) -> Result<Option<(usize, u8)>, DecodeError> {
6015 loop {
6016 if self.index == self.input.len() {
6017 return Ok(None);
6018 }
6019
6020 if self.starts_with_line_ending() {
6021 let line_end_index = self.index;
6022 if self.line_len == 0 {
6023 return Err(DecodeError::InvalidLineWrap {
6024 index: line_end_index,
6025 });
6026 }
6027
6028 self.index += self.wrap.line_ending.byte_len();
6029 if self.index == self.input.len() {
6030 self.line_len = 0;
6031 return Ok(None);
6032 }
6033
6034 if self.line_len != self.wrap.line_len {
6035 return Err(DecodeError::InvalidLineWrap {
6036 index: line_end_index,
6037 });
6038 }
6039 self.line_len = 0;
6040 continue;
6041 }
6042
6043 let byte = self.input[self.index];
6044 if matches!(byte, b'\r' | b'\n') {
6045 return Err(DecodeError::InvalidLineWrap { index: self.index });
6046 }
6047
6048 self.line_len += 1;
6049 if self.line_len > self.wrap.line_len {
6050 return Err(DecodeError::InvalidLineWrap { index: self.index });
6051 }
6052
6053 let index = self.index;
6054 self.index += 1;
6055 return Ok(Some((index, byte)));
6056 }
6057 }
6058
6059 fn starts_with_line_ending(&self) -> bool {
6060 let line_ending = self.wrap.line_ending.as_bytes();
6061 let end = self.index + line_ending.len();
6062 end <= self.input.len() && &self.input[self.index..end] == line_ending
6063 }
6064}
6065
6066fn validate_wrapped_decode<A: Alphabet, const PAD: bool>(
6067 input: &[u8],
6068 wrap: LineWrap,
6069) -> Result<usize, DecodeError> {
6070 let mut bytes = WrappedBytes::new(input, wrap)?;
6071 let mut chunk = [0u8; 4];
6072 let mut indexes = [0usize; 4];
6073 let mut chunk_len = 0;
6074 let mut required = 0;
6075 let mut terminal_seen = false;
6076
6077 while let Some((index, byte)) = bytes.next_byte()? {
6078 if terminal_seen {
6079 return Err(DecodeError::InvalidPadding { index });
6080 }
6081
6082 chunk[chunk_len] = byte;
6083 indexes[chunk_len] = index;
6084 chunk_len += 1;
6085
6086 if chunk_len == 4 {
6087 let written =
6088 validate_chunk::<A, PAD>(chunk).map_err(|err| map_chunk_error(err, &indexes))?;
6089 required += written;
6090 terminal_seen = written < 3;
6091 chunk_len = 0;
6092 }
6093 }
6094
6095 if chunk_len == 0 {
6096 return Ok(required);
6097 }
6098 if PAD {
6099 return Err(DecodeError::InvalidLength);
6100 }
6101
6102 validate_tail_unpadded::<A>(&chunk[..chunk_len])
6103 .map_err(|err| map_partial_chunk_error(err, &indexes, chunk_len))?;
6104 Ok(required + decoded_capacity(chunk_len))
6105}
6106
6107fn decode_wrapped_to_slice<A: Alphabet, const PAD: bool>(
6108 input: &[u8],
6109 output: &mut [u8],
6110 wrap: LineWrap,
6111) -> Result<usize, DecodeError> {
6112 let mut bytes = WrappedBytes::new(input, wrap)?;
6113 let mut chunk = [0u8; 4];
6114 let mut indexes = [0usize; 4];
6115 let mut chunk_len = 0;
6116 let mut write = 0;
6117 let mut terminal_seen = false;
6118
6119 while let Some((index, byte)) = bytes.next_byte()? {
6120 if terminal_seen {
6121 return Err(DecodeError::InvalidPadding { index });
6122 }
6123
6124 chunk[chunk_len] = byte;
6125 indexes[chunk_len] = index;
6126 chunk_len += 1;
6127
6128 if chunk_len == 4 {
6129 let available = output.len();
6130 let output_tail = output.get_mut(write..).ok_or(DecodeError::OutputTooSmall {
6131 required: write,
6132 available,
6133 })?;
6134 let written = decode_chunk::<A, PAD>(chunk, output_tail)
6135 .map_err(|err| map_chunk_error(err, &indexes))?;
6136 write += written;
6137 terminal_seen = written < 3;
6138 chunk_len = 0;
6139 }
6140 }
6141
6142 if chunk_len == 0 {
6143 return Ok(write);
6144 }
6145 if PAD {
6146 return Err(DecodeError::InvalidLength);
6147 }
6148
6149 decode_tail_unpadded::<A>(&chunk[..chunk_len], &mut output[write..])
6150 .map_err(|err| map_partial_chunk_error(err, &indexes, chunk_len))
6151 .map(|n| write + n)
6152}
6153
6154fn compact_wrapped_input(buffer: &mut [u8], wrap: LineWrap) -> Result<usize, DecodeError> {
6155 if !wrap.is_valid() {
6156 return Err(DecodeError::InvalidLineWrap { index: 0 });
6157 }
6158
6159 let line_ending = wrap.line_ending.as_bytes();
6160 let line_ending_len = line_ending.len();
6161 let mut read = 0;
6162 let mut write = 0;
6163
6164 while read < buffer.len() {
6165 let line_end = read + line_ending_len;
6166 if buffer.get(read..line_end) == Some(line_ending) {
6167 read = line_end;
6168 continue;
6169 }
6170
6171 buffer[write] = buffer[read];
6172 write += 1;
6173 read += 1;
6174 }
6175
6176 Ok(write)
6177}
6178
6179#[inline]
6180const fn is_legacy_whitespace(byte: u8) -> bool {
6181 matches!(byte, b' ' | b'\t' | b'\r' | b'\n')
6182}
6183
6184fn map_chunk_error(err: DecodeError, indexes: &[usize; 4]) -> DecodeError {
6185 match err {
6186 DecodeError::InvalidByte { index, byte } => DecodeError::InvalidByte {
6187 index: indexes[index],
6188 byte,
6189 },
6190 DecodeError::InvalidPadding { index } => DecodeError::InvalidPadding {
6191 index: indexes[index],
6192 },
6193 DecodeError::InvalidInput
6194 | DecodeError::InvalidLineWrap { .. }
6195 | DecodeError::InvalidLength
6196 | DecodeError::OutputTooSmall { .. } => err,
6197 }
6198}
6199
6200fn map_partial_chunk_error(err: DecodeError, indexes: &[usize; 4], len: usize) -> DecodeError {
6201 match err {
6202 DecodeError::InvalidByte { index, byte } if index < len => DecodeError::InvalidByte {
6203 index: indexes[index],
6204 byte,
6205 },
6206 DecodeError::InvalidPadding { index } if index < len => DecodeError::InvalidPadding {
6207 index: indexes[index],
6208 },
6209 DecodeError::InvalidByte { .. }
6210 | DecodeError::InvalidPadding { .. }
6211 | DecodeError::InvalidLineWrap { .. }
6212 | DecodeError::InvalidInput
6213 | DecodeError::InvalidLength
6214 | DecodeError::OutputTooSmall { .. } => err,
6215 }
6216}
6217
6218fn decode_padded<A: Alphabet>(input: &[u8], output: &mut [u8]) -> Result<usize, DecodeError> {
6219 if !input.len().is_multiple_of(4) {
6220 return Err(DecodeError::InvalidLength);
6221 }
6222 let required = decoded_len_padded(input)?;
6223 if output.len() < required {
6224 return Err(DecodeError::OutputTooSmall {
6225 required,
6226 available: output.len(),
6227 });
6228 }
6229
6230 let mut read = 0;
6231 let mut write = 0;
6232 while read < input.len() {
6233 let chunk = read_quad(input, read)?;
6234 let available = output.len();
6235 let output_tail = output.get_mut(write..).ok_or(DecodeError::OutputTooSmall {
6236 required: write,
6237 available,
6238 })?;
6239 let written = decode_chunk::<A, true>(chunk, output_tail)
6240 .map_err(|err| err.with_index_offset(read))?;
6241 read += 4;
6242 write += written;
6243 if written < 3 && read != input.len() {
6244 return Err(DecodeError::InvalidPadding { index: read - 4 });
6245 }
6246 }
6247 Ok(write)
6248}
6249
6250fn validate_decode<A: Alphabet, const PAD: bool>(input: &[u8]) -> Result<usize, DecodeError> {
6251 if input.is_empty() {
6252 return Ok(0);
6253 }
6254
6255 if PAD {
6256 validate_padded::<A>(input)
6257 } else {
6258 validate_unpadded::<A>(input)
6259 }
6260}
6261
6262fn validate_padded<A: Alphabet>(input: &[u8]) -> Result<usize, DecodeError> {
6263 if !input.len().is_multiple_of(4) {
6264 return Err(DecodeError::InvalidLength);
6265 }
6266 let required = decoded_len_padded(input)?;
6267
6268 let mut read = 0;
6269 while read < input.len() {
6270 let chunk = read_quad(input, read)?;
6271 let written =
6272 validate_chunk::<A, true>(chunk).map_err(|err| err.with_index_offset(read))?;
6273 read += 4;
6274 if written < 3 && read != input.len() {
6275 return Err(DecodeError::InvalidPadding { index: read - 4 });
6276 }
6277 }
6278
6279 Ok(required)
6280}
6281
6282fn validate_unpadded<A: Alphabet>(input: &[u8]) -> Result<usize, DecodeError> {
6283 let required = decoded_len_unpadded(input)?;
6284
6285 let mut read = 0;
6286 while read + 4 <= input.len() {
6287 let chunk = read_quad(input, read)?;
6288 validate_chunk::<A, false>(chunk).map_err(|err| err.with_index_offset(read))?;
6289 read += 4;
6290 }
6291 validate_tail_unpadded::<A>(&input[read..]).map_err(|err| err.with_index_offset(read))?;
6292
6293 Ok(required)
6294}
6295
6296fn decode_unpadded<A: Alphabet>(input: &[u8], output: &mut [u8]) -> Result<usize, DecodeError> {
6297 let required = decoded_len_unpadded(input)?;
6298 if output.len() < required {
6299 return Err(DecodeError::OutputTooSmall {
6300 required,
6301 available: output.len(),
6302 });
6303 }
6304
6305 let mut read = 0;
6306 let mut write = 0;
6307 while read + 4 <= input.len() {
6308 let chunk = read_quad(input, read)?;
6309 let available = output.len();
6310 let output_tail = output.get_mut(write..).ok_or(DecodeError::OutputTooSmall {
6311 required: write,
6312 available,
6313 })?;
6314 let written = decode_chunk::<A, false>(chunk, output_tail)
6315 .map_err(|err| err.with_index_offset(read))?;
6316 read += 4;
6317 write += written;
6318 }
6319 decode_tail_unpadded::<A>(&input[read..], &mut output[write..])
6320 .map_err(|err| err.with_index_offset(read))
6321 .map(|n| write + n)
6322}
6323
6324fn decoded_len_padded(input: &[u8]) -> Result<usize, DecodeError> {
6325 if input.is_empty() {
6326 return Ok(0);
6327 }
6328 if !input.len().is_multiple_of(4) {
6329 return Err(DecodeError::InvalidLength);
6330 }
6331
6332 let Some((&last, before_last_prefix)) = input.split_last() else {
6333 return Ok(0);
6334 };
6335 let Some(&before_last) = before_last_prefix.last() else {
6336 return Err(DecodeError::InvalidLength);
6337 };
6338
6339 let mut padding = 0;
6340 if last == b'=' {
6341 padding += 1;
6342 }
6343 if before_last == b'=' {
6344 padding += 1;
6345 }
6346 if padding == 0
6347 && let Some(index) = input.iter().position(|byte| *byte == b'=')
6348 {
6349 return Err(DecodeError::InvalidPadding { index });
6350 }
6351 if padding > 0 {
6352 let first_pad = input.len() - padding;
6353 if let Some(index) = input[..first_pad].iter().position(|byte| *byte == b'=') {
6354 return Err(DecodeError::InvalidPadding { index });
6355 }
6356 }
6357 Ok(input.len() / 4 * 3 - padding)
6358}
6359
6360fn decoded_len_unpadded(input: &[u8]) -> Result<usize, DecodeError> {
6361 if input.len() % 4 == 1 {
6362 return Err(DecodeError::InvalidLength);
6363 }
6364 if let Some(index) = input.iter().position(|byte| *byte == b'=') {
6365 return Err(DecodeError::InvalidPadding { index });
6366 }
6367 Ok(decoded_capacity(input.len()))
6368}
6369
6370fn read_quad(input: &[u8], offset: usize) -> Result<[u8; 4], DecodeError> {
6371 let end = offset.checked_add(4).ok_or(DecodeError::InvalidLength)?;
6372 match input.get(offset..end) {
6373 Some([b0, b1, b2, b3]) => Ok([*b0, *b1, *b2, *b3]),
6374 _ => Err(DecodeError::InvalidLength),
6375 }
6376}
6377
6378fn first_padding_index(input: [u8; 4]) -> usize {
6379 let [b0, b1, b2, b3] = input;
6380 if b0 == b'=' {
6381 0
6382 } else if b1 == b'=' {
6383 1
6384 } else if b2 == b'=' {
6385 2
6386 } else if b3 == b'=' {
6387 3
6388 } else {
6389 0
6390 }
6391}
6392
6393fn validate_chunk<A: Alphabet, const PAD: bool>(input: [u8; 4]) -> Result<usize, DecodeError> {
6394 let [b0, b1, b2, b3] = input;
6395 let _v0 = decode_byte::<A>(b0, 0)?;
6396 let v1 = decode_byte::<A>(b1, 1)?;
6397
6398 match (b2, b3) {
6399 (b'=', b'=') if PAD => {
6400 if v1 & 0b0000_1111 != 0 {
6401 return Err(DecodeError::InvalidPadding { index: 1 });
6402 }
6403 Ok(1)
6404 }
6405 (b'=', _) if PAD => Err(DecodeError::InvalidPadding { index: 2 }),
6406 (_, b'=') if PAD => {
6407 let v2 = decode_byte::<A>(b2, 2)?;
6408 if v2 & 0b0000_0011 != 0 {
6409 return Err(DecodeError::InvalidPadding { index: 2 });
6410 }
6411 Ok(2)
6412 }
6413 (b'=', _) | (_, b'=') => Err(DecodeError::InvalidPadding {
6414 index: first_padding_index(input),
6415 }),
6416 _ => {
6417 decode_byte::<A>(b2, 2)?;
6418 decode_byte::<A>(b3, 3)?;
6419 Ok(3)
6420 }
6421 }
6422}
6423
6424fn decode_chunk<A: Alphabet, const PAD: bool>(
6425 input: [u8; 4],
6426 output: &mut [u8],
6427) -> Result<usize, DecodeError> {
6428 let [b0, b1, b2, b3] = input;
6429 let v0 = decode_byte::<A>(b0, 0)?;
6430 let v1 = decode_byte::<A>(b1, 1)?;
6431
6432 match (b2, b3) {
6433 (b'=', b'=') if PAD => {
6434 if output.is_empty() {
6435 return Err(DecodeError::OutputTooSmall {
6436 required: 1,
6437 available: output.len(),
6438 });
6439 }
6440 if v1 & 0b0000_1111 != 0 {
6441 return Err(DecodeError::InvalidPadding { index: 1 });
6442 }
6443 output[0] = (v0 << 2) | (v1 >> 4);
6444 Ok(1)
6445 }
6446 (b'=', _) if PAD => Err(DecodeError::InvalidPadding { index: 2 }),
6447 (_, b'=') if PAD => {
6448 if output.len() < 2 {
6449 return Err(DecodeError::OutputTooSmall {
6450 required: 2,
6451 available: output.len(),
6452 });
6453 }
6454 let v2 = decode_byte::<A>(b2, 2)?;
6455 if v2 & 0b0000_0011 != 0 {
6456 return Err(DecodeError::InvalidPadding { index: 2 });
6457 }
6458 output[0] = (v0 << 2) | (v1 >> 4);
6459 output[1] = (v1 << 4) | (v2 >> 2);
6460 Ok(2)
6461 }
6462 (b'=', _) | (_, b'=') => Err(DecodeError::InvalidPadding {
6463 index: first_padding_index(input),
6464 }),
6465 _ => {
6466 if output.len() < 3 {
6467 return Err(DecodeError::OutputTooSmall {
6468 required: 3,
6469 available: output.len(),
6470 });
6471 }
6472 let v2 = decode_byte::<A>(b2, 2)?;
6473 let v3 = decode_byte::<A>(b3, 3)?;
6474 output[0] = (v0 << 2) | (v1 >> 4);
6475 output[1] = (v1 << 4) | (v2 >> 2);
6476 output[2] = (v2 << 6) | v3;
6477 Ok(3)
6478 }
6479 }
6480}
6481
6482fn validate_tail_unpadded<A: Alphabet>(input: &[u8]) -> Result<(), DecodeError> {
6483 match input {
6484 [] => Ok(()),
6485 [b0, b1] => {
6486 decode_byte::<A>(*b0, 0)?;
6487 let v1 = decode_byte::<A>(*b1, 1)?;
6488 if v1 & 0b0000_1111 != 0 {
6489 return Err(DecodeError::InvalidPadding { index: 1 });
6490 }
6491 Ok(())
6492 }
6493 [b0, b1, b2] => {
6494 decode_byte::<A>(*b0, 0)?;
6495 decode_byte::<A>(*b1, 1)?;
6496 let v2 = decode_byte::<A>(*b2, 2)?;
6497 if v2 & 0b0000_0011 != 0 {
6498 return Err(DecodeError::InvalidPadding { index: 2 });
6499 }
6500 Ok(())
6501 }
6502 _ => Err(DecodeError::InvalidLength),
6503 }
6504}
6505
6506fn decode_tail_unpadded<A: Alphabet>(
6507 input: &[u8],
6508 output: &mut [u8],
6509) -> Result<usize, DecodeError> {
6510 match input {
6511 [] => Ok(0),
6512 [b0, b1] => {
6513 let Some(out0) = output.first_mut() else {
6514 return Err(DecodeError::OutputTooSmall {
6515 required: 1,
6516 available: output.len(),
6517 });
6518 };
6519 let v0 = decode_byte::<A>(*b0, 0)?;
6520 let v1 = decode_byte::<A>(*b1, 1)?;
6521 if v1 & 0b0000_1111 != 0 {
6522 return Err(DecodeError::InvalidPadding { index: 1 });
6523 }
6524 *out0 = (v0 << 2) | (v1 >> 4);
6525 Ok(1)
6526 }
6527 [b0, b1, b2] => {
6528 let available = output.len();
6529 let Some([out0, out1]) = output.get_mut(..2) else {
6530 return Err(DecodeError::OutputTooSmall {
6531 required: 2,
6532 available,
6533 });
6534 };
6535 let v0 = decode_byte::<A>(*b0, 0)?;
6536 let v1 = decode_byte::<A>(*b1, 1)?;
6537 let v2 = decode_byte::<A>(*b2, 2)?;
6538 if v2 & 0b0000_0011 != 0 {
6539 return Err(DecodeError::InvalidPadding { index: 2 });
6540 }
6541 *out0 = (v0 << 2) | (v1 >> 4);
6542 *out1 = (v1 << 4) | (v2 >> 2);
6543 Ok(2)
6544 }
6545 _ => Err(DecodeError::InvalidLength),
6546 }
6547}
6548
6549fn decode_byte<A: Alphabet>(byte: u8, index: usize) -> Result<u8, DecodeError> {
6550 A::decode(byte).ok_or(DecodeError::InvalidByte { index, byte })
6551}
6552
6553fn ct_decode_slice<A: Alphabet, const PAD: bool>(
6554 input: &[u8],
6555 output: &mut [u8],
6556) -> Result<usize, DecodeError> {
6557 if input.is_empty() {
6558 return Ok(0);
6559 }
6560
6561 if PAD {
6562 ct_decode_padded::<A>(input, output)
6563 } else {
6564 ct_decode_unpadded::<A>(input, output)
6565 }
6566}
6567
6568fn ct_decode_in_place<A: Alphabet, const PAD: bool>(
6569 buffer: &mut [u8],
6570) -> Result<usize, DecodeError> {
6571 if buffer.is_empty() {
6572 return Ok(0);
6573 }
6574
6575 if PAD {
6576 ct_decode_padded_in_place::<A>(buffer)
6577 } else {
6578 ct_decode_unpadded_in_place::<A>(buffer)
6579 }
6580}
6581
6582fn ct_validate_decode<A: Alphabet, const PAD: bool>(input: &[u8]) -> Result<(), DecodeError> {
6583 if input.is_empty() {
6584 return Ok(());
6585 }
6586
6587 if PAD {
6588 ct_validate_padded::<A>(input)
6589 } else {
6590 ct_validate_unpadded::<A>(input)
6591 }
6592}
6593
6594fn ct_decoded_len<A: Alphabet, const PAD: bool>(input: &[u8]) -> Result<usize, DecodeError> {
6595 ct_validate_decode::<A, PAD>(input)?;
6596 if input.is_empty() {
6597 return Ok(0);
6598 }
6599
6600 if PAD {
6601 Ok(input.len() / 4 * 3 - ct_padding_len(input))
6602 } else {
6603 let full_quads = input.len() / 4 * 3;
6604 match input.len() % 4 {
6605 0 => Ok(full_quads),
6606 2 => Ok(full_quads + 1),
6607 3 => Ok(full_quads + 2),
6608 _ => Err(DecodeError::InvalidLength),
6609 }
6610 }
6611}
6612
6613fn ct_validate_padded<A: Alphabet>(input: &[u8]) -> Result<(), DecodeError> {
6614 if !input.len().is_multiple_of(4) {
6615 return Err(DecodeError::InvalidLength);
6616 }
6617
6618 let padding = ct_padding_len(input);
6619 let mut invalid_byte = 0u8;
6620 let mut invalid_padding = 0u8;
6621 let mut read = 0;
6622
6623 while read + 4 < input.len() {
6624 let [b0, b1, b2, b3] = read_quad(input, read)?;
6625 let (_, valid0) = ct_decode_alphabet_byte::<A>(b0);
6626 let (_, valid1) = ct_decode_alphabet_byte::<A>(b1);
6627 let (_, valid2) = ct_decode_alphabet_byte::<A>(b2);
6628 let (_, valid3) = ct_decode_alphabet_byte::<A>(b3);
6629
6630 invalid_byte |= !valid0;
6631 invalid_byte |= !valid1;
6632 invalid_byte |= !valid2;
6633 invalid_byte |= !valid3;
6634 invalid_padding |= ct_mask_eq_u8(b2, b'=');
6635 invalid_padding |= ct_mask_eq_u8(b3, b'=');
6636 read += 4;
6637 }
6638
6639 let final_chunk = read_quad(input, read)?;
6640 let (_, final_invalid_byte, final_invalid_padding, _) =
6641 ct_padded_final_quantum::<A>(final_chunk, padding);
6642 invalid_byte |= final_invalid_byte;
6643 invalid_padding |= final_invalid_padding;
6644
6645 report_ct_error(invalid_byte, invalid_padding)
6646}
6647
6648fn ct_validate_unpadded<A: Alphabet>(input: &[u8]) -> Result<(), DecodeError> {
6649 if input.len() % 4 == 1 {
6650 return Err(DecodeError::InvalidLength);
6651 }
6652
6653 let mut invalid_byte = 0u8;
6654 let mut invalid_padding = 0u8;
6655 let mut read = 0;
6656
6657 while read + 4 <= input.len() {
6658 let [b0, b1, b2, b3] = read_quad(input, read)?;
6659 let (_, valid0) = ct_decode_alphabet_byte::<A>(b0);
6660 let (_, valid1) = ct_decode_alphabet_byte::<A>(b1);
6661 let (_, valid2) = ct_decode_alphabet_byte::<A>(b2);
6662 let (_, valid3) = ct_decode_alphabet_byte::<A>(b3);
6663
6664 invalid_byte |= !valid0;
6665 invalid_byte |= !valid1;
6666 invalid_byte |= !valid2;
6667 invalid_byte |= !valid3;
6668 invalid_padding |= ct_mask_eq_u8(b0, b'=');
6669 invalid_padding |= ct_mask_eq_u8(b1, b'=');
6670 invalid_padding |= ct_mask_eq_u8(b2, b'=');
6671 invalid_padding |= ct_mask_eq_u8(b3, b'=');
6672
6673 read += 4;
6674 }
6675
6676 match input.get(read..) {
6677 Some([]) => {}
6678 Some([b0, b1]) => {
6679 let (_, valid0) = ct_decode_alphabet_byte::<A>(*b0);
6680 let (v1, valid1) = ct_decode_alphabet_byte::<A>(*b1);
6681 invalid_byte |= !valid0;
6682 invalid_byte |= !valid1;
6683 invalid_padding |= ct_mask_eq_u8(*b0, b'=');
6684 invalid_padding |= ct_mask_eq_u8(*b1, b'=');
6685 invalid_padding |= ct_mask_nonzero_u8(v1 & 0b0000_1111);
6686 }
6687 Some([b0, b1, b2]) => {
6688 let (_, valid0) = ct_decode_alphabet_byte::<A>(*b0);
6689 let (_, valid1) = ct_decode_alphabet_byte::<A>(*b1);
6690 let (v2, valid2) = ct_decode_alphabet_byte::<A>(*b2);
6691 invalid_byte |= !valid0;
6692 invalid_byte |= !valid1;
6693 invalid_byte |= !valid2;
6694 invalid_padding |= ct_mask_eq_u8(*b0, b'=');
6695 invalid_padding |= ct_mask_eq_u8(*b1, b'=');
6696 invalid_padding |= ct_mask_eq_u8(*b2, b'=');
6697 invalid_padding |= ct_mask_nonzero_u8(v2 & 0b0000_0011);
6698 }
6699 _ => return Err(DecodeError::InvalidLength),
6700 }
6701
6702 report_ct_error(invalid_byte, invalid_padding)
6703}
6704
6705fn ct_padded_final_quantum<A: Alphabet>(
6706 input: [u8; 4],
6707 padding: usize,
6708) -> ([u8; 3], u8, u8, usize) {
6709 let [b0, b1, b2, b3] = input;
6710 let (v0, valid0) = ct_decode_alphabet_byte::<A>(b0);
6711 let (v1, valid1) = ct_decode_alphabet_byte::<A>(b1);
6712 let (v2, valid2) = ct_decode_alphabet_byte::<A>(b2);
6713 let (v3, valid3) = ct_decode_alphabet_byte::<A>(b3);
6714
6715 let padding_byte = padding.to_le_bytes()[0];
6716 let no_padding = ct_mask_eq_u8(padding_byte, 0);
6717 let one_padding = ct_mask_eq_u8(padding_byte, 1);
6718 let two_padding = ct_mask_eq_u8(padding_byte, 2);
6719 let require_v2 = no_padding | one_padding;
6720 let require_v3 = no_padding;
6721
6722 let invalid_byte = !valid0 | !valid1 | (!valid2 & require_v2) | (!valid3 & require_v3);
6723 let invalid_padding = (ct_mask_nonzero_u8(v1 & 0b0000_1111) & two_padding)
6724 | ((ct_mask_eq_u8(b2, b'=') | ct_mask_nonzero_u8(v2 & 0b0000_0011)) & one_padding)
6725 | ((ct_mask_eq_u8(b2, b'=') | ct_mask_eq_u8(b3, b'=')) & no_padding);
6726
6727 (
6728 [(v0 << 2) | (v1 >> 4), (v1 << 4) | (v2 >> 2), (v2 << 6) | v3],
6729 invalid_byte,
6730 invalid_padding,
6731 3 - padding,
6732 )
6733}
6734
6735fn ct_decode_padded<A: Alphabet>(input: &[u8], output: &mut [u8]) -> Result<usize, DecodeError> {
6736 if !input.len().is_multiple_of(4) {
6737 return Err(DecodeError::InvalidLength);
6738 }
6739
6740 let padding = ct_padding_len(input);
6741 let required = input.len() / 4 * 3 - padding;
6742 if output.len() < required {
6743 return Err(DecodeError::OutputTooSmall {
6744 required,
6745 available: output.len(),
6746 });
6747 }
6748
6749 let mut invalid_byte = 0u8;
6750 let mut invalid_padding = 0u8;
6751 let mut write = 0;
6752 let mut read = 0;
6753
6754 while read + 4 < input.len() {
6755 let [b0, b1, b2, b3] = read_quad(input, read)?;
6756 let (v0, valid0) = ct_decode_alphabet_byte::<A>(b0);
6757 let (v1, valid1) = ct_decode_alphabet_byte::<A>(b1);
6758 let (v2, valid2) = ct_decode_alphabet_byte::<A>(b2);
6759 let (v3, valid3) = ct_decode_alphabet_byte::<A>(b3);
6760
6761 invalid_byte |= !valid0;
6762 invalid_byte |= !valid1;
6763 invalid_byte |= !valid2;
6764 invalid_byte |= !valid3;
6765 invalid_padding |= ct_mask_eq_u8(b2, b'=');
6766 invalid_padding |= ct_mask_eq_u8(b3, b'=');
6767 output[write] = (v0 << 2) | (v1 >> 4);
6768 output[write + 1] = (v1 << 4) | (v2 >> 2);
6769 output[write + 2] = (v2 << 6) | v3;
6770 write += 3;
6771 read += 4;
6772 }
6773
6774 let final_chunk = read_quad(input, read)?;
6775 let (final_bytes, final_invalid_byte, final_invalid_padding, final_written) =
6776 ct_padded_final_quantum::<A>(final_chunk, padding);
6777 invalid_byte |= final_invalid_byte;
6778 invalid_padding |= final_invalid_padding;
6779 output[write..write + final_written].copy_from_slice(&final_bytes[..final_written]);
6780 write += final_written;
6781
6782 report_ct_error(invalid_byte, invalid_padding)?;
6783 Ok(write)
6784}
6785
6786fn ct_decode_padded_in_place<A: Alphabet>(buffer: &mut [u8]) -> Result<usize, DecodeError> {
6787 if !buffer.len().is_multiple_of(4) {
6788 return Err(DecodeError::InvalidLength);
6789 }
6790
6791 let padding = ct_padding_len(buffer);
6792 let required = buffer.len() / 4 * 3 - padding;
6793 debug_assert!(required <= buffer.len());
6794
6795 let mut invalid_byte = 0u8;
6796 let mut invalid_padding = 0u8;
6797 let mut write = 0;
6798 let mut read = 0;
6799
6800 while read + 4 < buffer.len() {
6801 let [b0, b1, b2, b3] = read_quad(buffer, read)?;
6802 let (v0, valid0) = ct_decode_alphabet_byte::<A>(b0);
6803 let (v1, valid1) = ct_decode_alphabet_byte::<A>(b1);
6804 let (v2, valid2) = ct_decode_alphabet_byte::<A>(b2);
6805 let (v3, valid3) = ct_decode_alphabet_byte::<A>(b3);
6806
6807 invalid_byte |= !valid0;
6808 invalid_byte |= !valid1;
6809 invalid_byte |= !valid2;
6810 invalid_byte |= !valid3;
6811 invalid_padding |= ct_mask_eq_u8(b2, b'=');
6812 invalid_padding |= ct_mask_eq_u8(b3, b'=');
6813 buffer[write] = (v0 << 2) | (v1 >> 4);
6814 buffer[write + 1] = (v1 << 4) | (v2 >> 2);
6815 buffer[write + 2] = (v2 << 6) | v3;
6816 write += 3;
6817 read += 4;
6818 }
6819
6820 let final_chunk = read_quad(buffer, read)?;
6821 let (final_bytes, final_invalid_byte, final_invalid_padding, final_written) =
6822 ct_padded_final_quantum::<A>(final_chunk, padding);
6823 invalid_byte |= final_invalid_byte;
6824 invalid_padding |= final_invalid_padding;
6825 buffer[write..write + final_written].copy_from_slice(&final_bytes[..final_written]);
6826 write += final_written;
6827
6828 debug_assert_eq!(write, required);
6829 report_ct_error(invalid_byte, invalid_padding)?;
6830 Ok(write)
6831}
6832
6833fn ct_decode_unpadded<A: Alphabet>(input: &[u8], output: &mut [u8]) -> Result<usize, DecodeError> {
6834 if input.len() % 4 == 1 {
6835 return Err(DecodeError::InvalidLength);
6836 }
6837
6838 let required = decoded_capacity(input.len());
6839 if output.len() < required {
6840 return Err(DecodeError::OutputTooSmall {
6841 required,
6842 available: output.len(),
6843 });
6844 }
6845
6846 let mut invalid_byte = 0u8;
6847 let mut invalid_padding = 0u8;
6848 let mut write = 0;
6849 let mut read = 0;
6850
6851 while read + 4 <= input.len() {
6852 let [b0, b1, b2, b3] = read_quad(input, read)?;
6853 let (v0, valid0) = ct_decode_alphabet_byte::<A>(b0);
6854 let (v1, valid1) = ct_decode_alphabet_byte::<A>(b1);
6855 let (v2, valid2) = ct_decode_alphabet_byte::<A>(b2);
6856 let (v3, valid3) = ct_decode_alphabet_byte::<A>(b3);
6857
6858 invalid_byte |= !valid0;
6859 invalid_byte |= !valid1;
6860 invalid_byte |= !valid2;
6861 invalid_byte |= !valid3;
6862 invalid_padding |= ct_mask_eq_u8(b0, b'=');
6863 invalid_padding |= ct_mask_eq_u8(b1, b'=');
6864 invalid_padding |= ct_mask_eq_u8(b2, b'=');
6865 invalid_padding |= ct_mask_eq_u8(b3, b'=');
6866
6867 output[write] = (v0 << 2) | (v1 >> 4);
6868 output[write + 1] = (v1 << 4) | (v2 >> 2);
6869 output[write + 2] = (v2 << 6) | v3;
6870 read += 4;
6871 write += 3;
6872 }
6873
6874 match input.get(read..) {
6875 Some([]) => {}
6876 Some([b0, b1]) => {
6877 let (v0, valid0) = ct_decode_alphabet_byte::<A>(*b0);
6878 let (v1, valid1) = ct_decode_alphabet_byte::<A>(*b1);
6879 invalid_byte |= !valid0;
6880 invalid_byte |= !valid1;
6881 invalid_padding |= ct_mask_eq_u8(*b0, b'=');
6882 invalid_padding |= ct_mask_eq_u8(*b1, b'=');
6883 invalid_padding |= ct_mask_nonzero_u8(v1 & 0b0000_1111);
6884 output[write] = (v0 << 2) | (v1 >> 4);
6885 write += 1;
6886 }
6887 Some([b0, b1, b2]) => {
6888 let (v0, valid0) = ct_decode_alphabet_byte::<A>(*b0);
6889 let (v1, valid1) = ct_decode_alphabet_byte::<A>(*b1);
6890 let (v2, valid2) = ct_decode_alphabet_byte::<A>(*b2);
6891 invalid_byte |= !valid0;
6892 invalid_byte |= !valid1;
6893 invalid_byte |= !valid2;
6894 invalid_padding |= ct_mask_eq_u8(*b0, b'=');
6895 invalid_padding |= ct_mask_eq_u8(*b1, b'=');
6896 invalid_padding |= ct_mask_eq_u8(*b2, b'=');
6897 invalid_padding |= ct_mask_nonzero_u8(v2 & 0b0000_0011);
6898 output[write] = (v0 << 2) | (v1 >> 4);
6899 output[write + 1] = (v1 << 4) | (v2 >> 2);
6900 write += 2;
6901 }
6902 _ => return Err(DecodeError::InvalidLength),
6903 }
6904
6905 report_ct_error(invalid_byte, invalid_padding)?;
6906 Ok(write)
6907}
6908
6909fn ct_decode_unpadded_in_place<A: Alphabet>(buffer: &mut [u8]) -> Result<usize, DecodeError> {
6910 if buffer.len() % 4 == 1 {
6911 return Err(DecodeError::InvalidLength);
6912 }
6913
6914 let required = decoded_capacity(buffer.len());
6915 debug_assert!(required <= buffer.len());
6916
6917 let mut invalid_byte = 0u8;
6918 let mut invalid_padding = 0u8;
6919 let mut write = 0;
6920 let mut read = 0;
6921
6922 while read + 4 <= buffer.len() {
6923 let [b0, b1, b2, b3] = read_quad(buffer, read)?;
6924 let (v0, valid0) = ct_decode_alphabet_byte::<A>(b0);
6925 let (v1, valid1) = ct_decode_alphabet_byte::<A>(b1);
6926 let (v2, valid2) = ct_decode_alphabet_byte::<A>(b2);
6927 let (v3, valid3) = ct_decode_alphabet_byte::<A>(b3);
6928
6929 invalid_byte |= !valid0;
6930 invalid_byte |= !valid1;
6931 invalid_byte |= !valid2;
6932 invalid_byte |= !valid3;
6933 invalid_padding |= ct_mask_eq_u8(b0, b'=');
6934 invalid_padding |= ct_mask_eq_u8(b1, b'=');
6935 invalid_padding |= ct_mask_eq_u8(b2, b'=');
6936 invalid_padding |= ct_mask_eq_u8(b3, b'=');
6937
6938 buffer[write] = (v0 << 2) | (v1 >> 4);
6939 buffer[write + 1] = (v1 << 4) | (v2 >> 2);
6940 buffer[write + 2] = (v2 << 6) | v3;
6941 read += 4;
6942 write += 3;
6943 }
6944
6945 match read_tail(buffer, read)? {
6946 [] => {}
6947 [b0, b1] => {
6948 let (v0, valid0) = ct_decode_alphabet_byte::<A>(*b0);
6949 let (v1, valid1) = ct_decode_alphabet_byte::<A>(*b1);
6950 invalid_byte |= !valid0;
6951 invalid_byte |= !valid1;
6952 invalid_padding |= ct_mask_eq_u8(*b0, b'=');
6953 invalid_padding |= ct_mask_eq_u8(*b1, b'=');
6954 invalid_padding |= ct_mask_nonzero_u8(v1 & 0b0000_1111);
6955 buffer[write] = (v0 << 2) | (v1 >> 4);
6956 write += 1;
6957 }
6958 [b0, b1, b2] => {
6959 let (v0, valid0) = ct_decode_alphabet_byte::<A>(*b0);
6960 let (v1, valid1) = ct_decode_alphabet_byte::<A>(*b1);
6961 let (v2, valid2) = ct_decode_alphabet_byte::<A>(*b2);
6962 invalid_byte |= !valid0;
6963 invalid_byte |= !valid1;
6964 invalid_byte |= !valid2;
6965 invalid_padding |= ct_mask_eq_u8(*b0, b'=');
6966 invalid_padding |= ct_mask_eq_u8(*b1, b'=');
6967 invalid_padding |= ct_mask_eq_u8(*b2, b'=');
6968 invalid_padding |= ct_mask_nonzero_u8(v2 & 0b0000_0011);
6969 buffer[write] = (v0 << 2) | (v1 >> 4);
6970 buffer[write + 1] = (v1 << 4) | (v2 >> 2);
6971 write += 2;
6972 }
6973 _ => return Err(DecodeError::InvalidLength),
6974 }
6975
6976 debug_assert_eq!(write, required);
6977 report_ct_error(invalid_byte, invalid_padding)?;
6978 Ok(write)
6979}
6980
6981fn read_tail(input: &[u8], offset: usize) -> Result<&[u8], DecodeError> {
6982 input.get(offset..).ok_or(DecodeError::InvalidLength)
6983}
6984
6985#[inline]
6986fn ct_decode_alphabet_byte<A: Alphabet>(byte: u8) -> (u8, u8) {
6987 let mut decoded = 0u8;
6988 let mut valid = 0u8;
6989 let mut candidate = 0u8;
6990
6991 while candidate < 64 {
6992 let matches = ct_mask_eq_u8(byte, A::ENCODE[candidate as usize]);
6993 decoded |= candidate & matches;
6994 valid |= matches;
6995 candidate += 1;
6996 }
6997
6998 (decoded, valid)
6999}
7000
7001fn ct_padding_len(input: &[u8]) -> usize {
7002 let Some((&last, before_last_prefix)) = input.split_last() else {
7003 return 0;
7004 };
7005 let Some(&before_last) = before_last_prefix.last() else {
7006 return 0;
7007 };
7008 usize::from(ct_mask_eq_u8(last, b'=') & 1) + usize::from(ct_mask_eq_u8(before_last, b'=') & 1)
7009}
7010
7011fn report_ct_error(invalid_byte: u8, invalid_padding: u8) -> Result<(), DecodeError> {
7012 if (invalid_byte | invalid_padding) != 0 {
7013 Err(DecodeError::InvalidInput)
7014 } else {
7015 Ok(())
7016 }
7017}
7018
7019#[cfg(kani)]
7020mod kani_proofs {
7021 use super::{
7022 STANDARD, Standard, checked_encoded_len, ct, decode_byte, decode_chunk,
7023 decode_tail_unpadded, decoded_capacity, validate_tail_unpadded,
7024 };
7025
7026 #[kani::proof]
7027 fn checked_encoded_len_is_bounded_for_small_inputs() {
7028 let len = usize::from(kani::any::<u8>());
7029 let padded = kani::any::<bool>();
7030 let encoded = checked_encoded_len(len, padded).expect("u8 input length cannot overflow");
7031
7032 assert!(encoded >= len);
7033 assert!(encoded <= len / 3 * 4 + 4);
7034 }
7035
7036 #[kani::proof]
7037 fn decoded_capacity_is_bounded_for_small_inputs() {
7038 let len = usize::from(kani::any::<u8>());
7039 let capacity = decoded_capacity(len);
7040
7041 assert!(capacity <= len / 4 * 3 + 2);
7042 }
7043
7044 #[kani::proof]
7045 #[kani::unwind(3)]
7046 fn standard_in_place_decode_returns_prefix_within_buffer() {
7047 let mut buffer = kani::any::<[u8; 8]>();
7048 let result = STANDARD.decode_in_place(&mut buffer);
7049
7050 if let Ok(decoded) = result {
7051 assert!(decoded.len() <= 8);
7052 }
7053 }
7054
7055 #[kani::proof]
7056 #[kani::unwind(3)]
7057 fn standard_decode_slice_returns_written_within_output() {
7058 let input = kani::any::<[u8; 4]>();
7059 let mut output = kani::any::<[u8; 3]>();
7060 let result = STANDARD.decode_slice(&input, &mut output);
7061
7062 if let Ok(written) = result {
7063 assert!(written <= output.len());
7064 }
7065 }
7066
7067 #[kani::proof]
7068 #[kani::unwind(3)]
7069 fn standard_decode_chunk_returns_written_within_output() {
7070 let input = kani::any::<[u8; 4]>();
7071 let mut output = kani::any::<[u8; 3]>();
7072 let result = decode_chunk::<Standard, true>(input, &mut output);
7073
7074 if let Ok(written) = result {
7075 assert!(written <= output.len());
7076 assert!(written <= 3);
7077 }
7078 }
7079
7080 #[kani::proof]
7081 #[kani::unwind(3)]
7082 fn standard_decode_chunk_bit_packing_matches_decoded_values() {
7083 let input = kani::any::<[u8; 4]>();
7084 let mut output = kani::any::<[u8; 3]>();
7085 let result = decode_chunk::<Standard, true>(input, &mut output);
7086
7087 if let Ok(written) = result {
7088 let v0 = decode_byte::<Standard>(input[0], 0).expect("successful chunk has v0");
7089 let v1 = decode_byte::<Standard>(input[1], 1).expect("successful chunk has v1");
7090
7091 assert!(output[0] == ((v0 << 2) | (v1 >> 4)));
7092
7093 if written >= 2 {
7094 let v2 = decode_byte::<Standard>(input[2], 2).expect("successful chunk has v2");
7095 assert!(output[1] == ((v1 << 4) | (v2 >> 2)));
7096 }
7097
7098 if written == 3 {
7099 let v2 = decode_byte::<Standard>(input[2], 2).expect("successful chunk has v2");
7100 let v3 = decode_byte::<Standard>(input[3], 3).expect("successful chunk has v3");
7101 assert!(output[2] == ((v2 << 6) | v3));
7102 }
7103 }
7104 }
7105
7106 #[kani::proof]
7107 #[kani::unwind(3)]
7108 fn standard_validate_tail_unpadded_accepts_or_rejects_without_panic() {
7109 let input = kani::any::<[u8; 3]>();
7110 let len = usize::from(kani::any::<u8>() % 4);
7111 let result = validate_tail_unpadded::<Standard>(&input[..len]);
7112
7113 if result.is_ok() {
7114 assert!(len == 0 || len == 2 || len == 3);
7115 }
7116 }
7117
7118 #[kani::proof]
7119 #[kani::unwind(3)]
7120 fn standard_decode_two_byte_tail_returns_written_within_output() {
7121 let input = kani::any::<[u8; 2]>();
7122 let mut output = kani::any::<[u8; 1]>();
7123 let result = decode_tail_unpadded::<Standard>(&input, &mut output);
7124
7125 if let Ok(written) = result {
7126 assert!(written <= output.len());
7127 assert!(written == 1);
7128 }
7129 }
7130
7131 #[kani::proof]
7132 #[kani::unwind(3)]
7133 fn standard_decode_three_byte_tail_returns_written_within_output() {
7134 let input = kani::any::<[u8; 3]>();
7135 let mut output = kani::any::<[u8; 2]>();
7136 let result = decode_tail_unpadded::<Standard>(&input, &mut output);
7137
7138 if let Ok(written) = result {
7139 assert!(written <= output.len());
7140 assert!(written == 2);
7141 }
7142 }
7143
7144 #[kani::proof]
7145 #[kani::unwind(3)]
7146 fn standard_decode_slice_clear_tail_clears_output_on_error() {
7147 let input = kani::any::<[u8; 4]>();
7148 let mut output = kani::any::<[u8; 3]>();
7149 let result = STANDARD.decode_slice_clear_tail(&input, &mut output);
7150
7151 if result.is_err() {
7152 assert!(output.iter().all(|byte| *byte == 0));
7153 }
7154 }
7155
7156 #[kani::proof]
7157 #[kani::unwind(3)]
7158 fn standard_encode_slice_returns_written_within_output() {
7159 let input = kani::any::<[u8; 3]>();
7160 let mut output = kani::any::<[u8; 4]>();
7161 let result = STANDARD.encode_slice(&input, &mut output);
7162
7163 if let Ok(written) = result {
7164 assert!(written <= output.len());
7165 }
7166 }
7167
7168 #[kani::proof]
7169 #[kani::unwind(4)]
7170 fn standard_encode_in_place_returns_prefix_within_buffer() {
7171 let mut buffer = kani::any::<[u8; 8]>();
7172 let input_len = usize::from(kani::any::<u8>() % 9);
7173 let result = STANDARD.encode_in_place(&mut buffer, input_len);
7174
7175 if let Ok(encoded) = result {
7176 assert!(encoded.len() <= 8);
7177 }
7178 }
7179
7180 #[kani::proof]
7181 #[kani::unwind(3)]
7182 fn standard_clear_tail_decode_clears_buffer_on_error() {
7183 let mut buffer = kani::any::<[u8; 4]>();
7184 let result = STANDARD.decode_in_place_clear_tail(&mut buffer);
7185
7186 if result.is_err() {
7187 assert!(buffer.iter().all(|byte| *byte == 0));
7188 }
7189 }
7190
7191 #[kani::proof]
7192 #[kani::unwind(3)]
7193 fn ct_standard_decode_slice_returns_written_within_output() {
7194 let input = kani::any::<[u8; 4]>();
7195 let mut output = kani::any::<[u8; 3]>();
7196 let result = ct::STANDARD.decode_slice(&input, &mut output);
7197
7198 if let Ok(written) = result {
7199 assert!(written <= output.len());
7200 }
7201 }
7202
7203 #[kani::proof]
7204 #[kani::unwind(3)]
7205 fn ct_standard_decode_slice_clear_tail_clears_output_on_error() {
7206 let input = kani::any::<[u8; 4]>();
7207 let mut output = kani::any::<[u8; 3]>();
7208 let result = ct::STANDARD.decode_slice_clear_tail(&input, &mut output);
7209
7210 if result.is_err() {
7211 assert!(output.iter().all(|byte| *byte == 0));
7212 }
7213 }
7214
7215 #[kani::proof]
7216 #[kani::unwind(3)]
7217 fn ct_standard_decode_in_place_clear_tail_clears_buffer_on_error() {
7218 let mut buffer = kani::any::<[u8; 4]>();
7219 let result = ct::STANDARD.decode_in_place_clear_tail(&mut buffer);
7220
7221 if result.is_err() {
7222 assert!(buffer.iter().all(|byte| *byte == 0));
7223 }
7224 }
7225
7226 #[kani::proof]
7227 #[kani::unwind(3)]
7228 fn ct_standard_validate_matches_decode_for_one_quantum() {
7229 let input = kani::any::<[u8; 4]>();
7230 let mut output = kani::any::<[u8; 3]>();
7231
7232 let validate_ok = ct::STANDARD.validate_result(&input).is_ok();
7233 let decode_ok = ct::STANDARD.decode_slice(&input, &mut output).is_ok();
7234
7235 assert!(validate_ok == decode_ok);
7236 }
7237}
7238
7239#[cfg(test)]
7240mod tests {
7241 use super::*;
7242
7243 fn fill_pattern(output: &mut [u8], seed: usize) {
7244 for (index, byte) in output.iter_mut().enumerate() {
7245 let value = (index * 73 + seed * 19) % 256;
7246 *byte = u8::try_from(value).unwrap();
7247 }
7248 }
7249
7250 fn assert_encode_backend_matches_scalar<A, const PAD: bool>(input: &[u8])
7251 where
7252 A: Alphabet,
7253 {
7254 let engine = Engine::<A, PAD>::new();
7255 let mut dispatched = [0x55; 256];
7256 let mut scalar = [0xaa; 256];
7257
7258 let dispatched_result = engine.encode_slice(input, &mut dispatched);
7259 let scalar_result = backend::scalar_reference_encode_slice::<A, PAD>(input, &mut scalar);
7260
7261 assert_eq!(dispatched_result, scalar_result);
7262 if let Ok(written) = dispatched_result {
7263 assert_eq!(&dispatched[..written], &scalar[..written]);
7264 }
7265
7266 let required = checked_encoded_len(input.len(), PAD).unwrap();
7267 if required > 0 {
7268 let mut dispatched_short = [0x55; 256];
7269 let mut scalar_short = [0xaa; 256];
7270 let available = required - 1;
7271
7272 assert_eq!(
7273 engine.encode_slice(input, &mut dispatched_short[..available]),
7274 backend::scalar_reference_encode_slice::<A, PAD>(
7275 input,
7276 &mut scalar_short[..available],
7277 )
7278 );
7279 }
7280 }
7281
7282 fn assert_decode_backend_matches_scalar<A, const PAD: bool>(input: &[u8])
7283 where
7284 A: Alphabet,
7285 {
7286 let engine = Engine::<A, PAD>::new();
7287 let mut dispatched = [0x55; 128];
7288 let mut scalar = [0xaa; 128];
7289
7290 let dispatched_result = engine.decode_slice(input, &mut dispatched);
7291 let scalar_result = backend::scalar_reference_decode_slice::<A, PAD>(input, &mut scalar);
7292
7293 assert_eq!(dispatched_result, scalar_result);
7294 if let Ok(written) = dispatched_result {
7295 assert_eq!(&dispatched[..written], &scalar[..written]);
7296
7297 if written > 0 {
7298 let mut dispatched_short = [0x55; 128];
7299 let mut scalar_short = [0xaa; 128];
7300 let available = written - 1;
7301
7302 assert_eq!(
7303 engine.decode_slice(input, &mut dispatched_short[..available]),
7304 backend::scalar_reference_decode_slice::<A, PAD>(
7305 input,
7306 &mut scalar_short[..available],
7307 )
7308 );
7309 }
7310 }
7311 }
7312
7313 fn assert_backend_round_trip_matches_scalar<A, const PAD: bool>(input: &[u8])
7314 where
7315 A: Alphabet,
7316 {
7317 assert_encode_backend_matches_scalar::<A, PAD>(input);
7318
7319 let mut encoded = [0; 256];
7320 let encoded_len =
7321 backend::scalar_reference_encode_slice::<A, PAD>(input, &mut encoded).unwrap();
7322 assert_decode_backend_matches_scalar::<A, PAD>(&encoded[..encoded_len]);
7323 }
7324
7325 fn assert_standard_decode_chunk_matches_input(input: &[u8]) {
7326 let mut encoded = [0u8; 4];
7327 let encoded_len = STANDARD.encode_slice(input, &mut encoded).unwrap();
7328 assert_eq!(encoded_len, 4);
7329
7330 let chunk = [encoded[0], encoded[1], encoded[2], encoded[3]];
7331 let mut decoded = [0u8; 3];
7332 let decoded_len = decode_chunk::<Standard, true>(chunk, &mut decoded).unwrap();
7333
7334 assert_eq!(decoded_len, input.len());
7335 assert_eq!(&decoded[..decoded_len], input);
7336 }
7337
7338 #[test]
7339 fn backend_dispatch_matches_scalar_reference_for_canonical_inputs() {
7340 let mut input = [0; 128];
7341
7342 for input_len in 0..=input.len() {
7343 fill_pattern(&mut input[..input_len], input_len);
7344 let input = &input[..input_len];
7345
7346 assert_backend_round_trip_matches_scalar::<Standard, true>(input);
7347 assert_backend_round_trip_matches_scalar::<Standard, false>(input);
7348 assert_backend_round_trip_matches_scalar::<UrlSafe, true>(input);
7349 assert_backend_round_trip_matches_scalar::<UrlSafe, false>(input);
7350 }
7351 }
7352
7353 #[test]
7354 fn backend_dispatch_matches_scalar_reference_for_malformed_inputs() {
7355 for input in [
7356 &b"Z"[..],
7357 b"====",
7358 b"AA=A",
7359 b"Zh==",
7360 b"Zm9=",
7361 b"Zm9v$g==",
7362 b"Zm9vZh==",
7363 ] {
7364 assert_decode_backend_matches_scalar::<Standard, true>(input);
7365 }
7366
7367 for input in [&b"Z"[..], b"AA=A", b"Zh", b"Zm9", b"Zm9vYg$"] {
7368 assert_decode_backend_matches_scalar::<Standard, false>(input);
7369 }
7370
7371 assert_decode_backend_matches_scalar::<UrlSafe, true>(b"AA+A");
7372 assert_decode_backend_matches_scalar::<UrlSafe, false>(b"AA/A");
7373 assert_decode_backend_matches_scalar::<Standard, true>(b"AA-A");
7374 assert_decode_backend_matches_scalar::<Standard, false>(b"AA_A");
7375 }
7376
7377 #[test]
7378 fn decode_chunk_bit_packing_matches_exhaustive_small_inputs() {
7379 for byte in u8::MIN..=u8::MAX {
7380 assert_standard_decode_chunk_matches_input(&[byte]);
7381 }
7382
7383 for first in u8::MIN..=u8::MAX {
7384 for second in u8::MIN..=u8::MAX {
7385 assert_standard_decode_chunk_matches_input(&[first, second]);
7386 }
7387 }
7388 }
7389
7390 #[test]
7391 fn decode_chunk_bit_packing_matches_representative_full_quanta() {
7392 const SAMPLES: [u8; 16] = [
7393 0, 1, 2, 15, 16, 31, 32, 63, 64, 95, 127, 128, 191, 192, 254, 255,
7394 ];
7395
7396 for first in SAMPLES {
7397 for second in SAMPLES {
7398 for third in SAMPLES {
7399 assert_standard_decode_chunk_matches_input(&[first, second, third]);
7400 }
7401 }
7402 }
7403 }
7404
7405 #[cfg(feature = "simd")]
7406 #[test]
7407 fn simd_dispatch_scaffold_keeps_scalar_active() {
7408 assert_eq!(simd::active_backend(), simd::ActiveBackend::Scalar);
7409 let _candidate = simd::detected_candidate();
7410 }
7411
7412 #[test]
7413 fn encodes_standard_vectors() {
7414 let vectors = [
7415 (&b""[..], &b""[..]),
7416 (&b"f"[..], &b"Zg=="[..]),
7417 (&b"fo"[..], &b"Zm8="[..]),
7418 (&b"foo"[..], &b"Zm9v"[..]),
7419 (&b"foob"[..], &b"Zm9vYg=="[..]),
7420 (&b"fooba"[..], &b"Zm9vYmE="[..]),
7421 (&b"foobar"[..], &b"Zm9vYmFy"[..]),
7422 ];
7423 for (input, expected) in vectors {
7424 let mut output = [0u8; 16];
7425 let written = STANDARD.encode_slice(input, &mut output).unwrap();
7426 assert_eq!(&output[..written], expected);
7427 }
7428 }
7429
7430 #[test]
7431 fn decodes_standard_vectors() {
7432 let vectors = [
7433 (&b""[..], &b""[..]),
7434 (&b"Zg=="[..], &b"f"[..]),
7435 (&b"Zm8="[..], &b"fo"[..]),
7436 (&b"Zm9v"[..], &b"foo"[..]),
7437 (&b"Zm9vYg=="[..], &b"foob"[..]),
7438 (&b"Zm9vYmE="[..], &b"fooba"[..]),
7439 (&b"Zm9vYmFy"[..], &b"foobar"[..]),
7440 ];
7441 for (input, expected) in vectors {
7442 let mut output = [0u8; 16];
7443 let written = STANDARD.decode_slice(input, &mut output).unwrap();
7444 assert_eq!(&output[..written], expected);
7445 }
7446 }
7447
7448 #[test]
7449 fn supports_unpadded_url_safe() {
7450 let mut encoded = [0u8; 16];
7451 let written = URL_SAFE_NO_PAD
7452 .encode_slice(b"\xfb\xff", &mut encoded)
7453 .unwrap();
7454 assert_eq!(&encoded[..written], b"-_8");
7455
7456 let mut decoded = [0u8; 2];
7457 let written = URL_SAFE_NO_PAD
7458 .decode_slice(&encoded[..written], &mut decoded)
7459 .unwrap();
7460 assert_eq!(&decoded[..written], b"\xfb\xff");
7461 }
7462
7463 #[test]
7464 fn decodes_in_place() {
7465 let mut buffer = *b"Zm9vYmFy";
7466 let decoded = STANDARD_NO_PAD.decode_in_place(&mut buffer).unwrap();
7467 assert_eq!(decoded, b"foobar");
7468 }
7469
7470 #[test]
7471 fn rejects_non_canonical_padding_bits() {
7472 let mut output = [0u8; 4];
7473 assert_eq!(
7474 STANDARD.decode_slice(b"Zh==", &mut output),
7475 Err(DecodeError::InvalidPadding { index: 1 })
7476 );
7477 assert_eq!(
7478 STANDARD.decode_slice(b"Zm9=", &mut output),
7479 Err(DecodeError::InvalidPadding { index: 2 })
7480 );
7481 }
7482}