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")]
78extern crate alloc;
79
80#[cfg(all(target_arch = "wasm32", not(feature = "allow-wasm32-best-effort-wipe")))]
81compile_error!(
82 "base64-ng: wasm32 builds use a compiler-fence-only wipe barrier that cannot \
83 constrain downstream wasm runtime JITs. Enable \
84 `allow-wasm32-best-effort-wipe` to accept this limitation and use \
85 caller-owned, platform-approved zeroization for high-assurance wasm deployments."
86);
87
88#[cfg(all(
89 not(miri),
90 not(feature = "allow-compiler-fence-only-wipe"),
91 not(any(
92 target_arch = "aarch64",
93 target_arch = "arm",
94 target_arch = "riscv32",
95 target_arch = "riscv64",
96 target_arch = "wasm32",
97 target_arch = "x86",
98 target_arch = "x86_64",
99 ))
100))]
101compile_error!(
102 "base64-ng: this architecture has no native hardware wipe barrier in \
103 base64-ng. Enable `allow-compiler-fence-only-wipe` only after reviewing \
104 docs/UNSAFE.md and applying platform-approved memory hygiene controls."
105);
106
107#[cfg(feature = "simd")]
108mod simd;
109
110pub mod runtime {
116 #[derive(Clone, Copy, Debug, Eq, PartialEq)]
118 #[non_exhaustive]
119 pub enum Backend {
120 Scalar,
122 Avx512Vbmi,
124 Avx2,
126 Ssse3Sse41,
128 Neon,
130 WasmSimd128,
132 }
133
134 impl Backend {
135 #[must_use]
141 pub const fn as_str(self) -> &'static str {
142 match self {
143 Self::Scalar => "scalar",
144 Self::Avx512Vbmi => "avx512-vbmi",
145 Self::Avx2 => "avx2",
146 Self::Ssse3Sse41 => "ssse3-sse4.1",
147 Self::Neon => "neon",
148 Self::WasmSimd128 => "wasm-simd128",
149 }
150 }
151
152 #[must_use]
165 pub const fn required_cpu_features(self) -> &'static [&'static str] {
166 match self {
167 Self::Scalar => &[],
168 Self::Avx512Vbmi => &["avx512f", "avx512bw", "avx512vl", "avx512vbmi"],
169 Self::Avx2 => &["avx2"],
170 Self::Ssse3Sse41 => &["ssse3", "sse4.1"],
171 Self::Neon => &["neon"],
172 Self::WasmSimd128 => &["simd128"],
173 }
174 }
175 }
176
177 impl core::fmt::Display for Backend {
178 fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
179 formatter.write_str(self.as_str())
180 }
181 }
182
183 #[derive(Clone, Copy, Debug, Eq, PartialEq)]
185 #[non_exhaustive]
186 pub enum CandidateDetectionMode {
187 SimdFeatureDisabled,
190 RuntimeCpuFeatures,
192 CompileTimeTargetFeatures,
197 }
198
199 impl CandidateDetectionMode {
200 #[must_use]
209 pub const fn as_str(self) -> &'static str {
210 match self {
211 Self::SimdFeatureDisabled => "simd-feature-disabled",
212 Self::RuntimeCpuFeatures => "runtime-cpu-features",
213 Self::CompileTimeTargetFeatures => "compile-time-target-features",
214 }
215 }
216 }
217
218 impl core::fmt::Display for CandidateDetectionMode {
219 fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
220 formatter.write_str(self.as_str())
221 }
222 }
223
224 #[derive(Clone, Copy, Debug, Eq, PartialEq)]
226 #[non_exhaustive]
227 pub enum SecurityPosture {
228 ScalarOnly,
230 SimdCandidateScalarActive,
232 Accelerated,
234 }
235
236 impl SecurityPosture {
237 #[must_use]
246 pub const fn as_str(self) -> &'static str {
247 match self {
248 Self::ScalarOnly => "scalar-only",
249 Self::SimdCandidateScalarActive => "simd-candidate-scalar-active",
250 Self::Accelerated => "accelerated",
251 }
252 }
253 }
254
255 impl core::fmt::Display for SecurityPosture {
256 fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
257 formatter.write_str(self.as_str())
258 }
259 }
260
261 #[derive(Clone, Copy, Debug, Eq, PartialEq)]
263 #[non_exhaustive]
264 pub enum WipePosture {
265 HardwareFence,
272 CompilerFenceOnly,
274 }
275
276 impl WipePosture {
277 #[must_use]
279 pub const fn as_str(self) -> &'static str {
280 match self {
281 Self::HardwareFence => "hardware-fence",
282 Self::CompilerFenceOnly => "compiler-fence-only",
283 }
284 }
285 }
286
287 impl core::fmt::Display for WipePosture {
288 fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
289 formatter.write_str(self.as_str())
290 }
291 }
292
293 #[derive(Clone, Copy, Debug, Eq, PartialEq)]
295 #[non_exhaustive]
296 pub enum CtGatePosture {
297 HardwareSpeculationBarrier,
304 OrderingFence,
307 CompilerFenceOnly,
309 }
310
311 impl CtGatePosture {
312 #[must_use]
314 pub const fn as_str(self) -> &'static str {
315 match self {
316 Self::HardwareSpeculationBarrier => "hardware-speculation-barrier",
317 Self::OrderingFence => "ordering-fence",
318 Self::CompilerFenceOnly => "compiler-fence-only",
319 }
320 }
321 }
322
323 impl core::fmt::Display for CtGatePosture {
324 fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
325 formatter.write_str(self.as_str())
326 }
327 }
328
329 #[derive(Clone, Copy, Debug, Eq, PartialEq)]
331 #[non_exhaustive]
332 pub enum BackendPolicy {
333 ScalarExecutionOnly,
335 SimdFeatureDisabled,
337 NoDetectedSimdCandidate,
339 HighAssuranceScalarOnly,
342 }
343
344 impl BackendPolicy {
345 #[must_use]
354 pub const fn as_str(self) -> &'static str {
355 match self {
356 Self::ScalarExecutionOnly => "scalar-execution-only",
357 Self::SimdFeatureDisabled => "simd-feature-disabled",
358 Self::NoDetectedSimdCandidate => "no-detected-simd-candidate",
359 Self::HighAssuranceScalarOnly => "high-assurance-scalar-only",
360 }
361 }
362 }
363
364 impl core::fmt::Display for BackendPolicy {
365 fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
366 formatter.write_str(self.as_str())
367 }
368 }
369
370 #[derive(Clone, Copy, Debug, Eq, PartialEq)]
372 pub struct BackendPolicyError {
373 pub policy: BackendPolicy,
375 pub report: BackendReport,
377 }
378
379 impl core::fmt::Display for BackendPolicyError {
380 fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
381 write!(
382 formatter,
383 "runtime backend policy `{}` was not satisfied ({})",
384 self.policy, self.report,
385 )
386 }
387 }
388
389 #[cfg(feature = "std")]
390 impl std::error::Error for BackendPolicyError {}
391
392 #[derive(Clone, Copy, Debug, Eq, PartialEq)]
394 pub struct BackendReport {
395 pub active: Backend,
397 pub candidate: Backend,
399 pub candidate_detection_mode: CandidateDetectionMode,
402 pub simd_feature_enabled: bool,
404 pub accelerated_backend_active: bool,
406 pub unsafe_boundary_enforced: bool,
413 pub security_posture: SecurityPosture,
415 pub wipe_posture: WipePosture,
417 pub ct_gate_posture: CtGatePosture,
419 }
420
421 #[derive(Clone, Copy, Debug, Eq, PartialEq)]
423 pub struct BackendSnapshot {
424 pub active: &'static str,
426 pub candidate: &'static str,
428 pub candidate_detection_mode: &'static str,
430 pub candidate_required_cpu_features: &'static [&'static str],
432 pub simd_feature_enabled: bool,
434 pub accelerated_backend_active: bool,
436 pub unsafe_boundary_enforced: bool,
442 pub security_posture: &'static str,
444 pub wipe_posture: &'static str,
446 pub ct_gate_posture: &'static str,
448 }
449
450 impl core::fmt::Display for BackendReport {
451 fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
452 write!(
453 formatter,
454 "active={} candidate={} candidate_detection_mode={} candidate_required_cpu_features=",
455 self.active, self.candidate, self.candidate_detection_mode,
456 )?;
457 write_feature_list(formatter, self.candidate_required_cpu_features())?;
458 write!(
459 formatter,
460 " simd_feature_enabled={} accelerated_backend_active={} unsafe_boundary_enforced={} security_posture={} wipe_posture={} ct_gate_posture={}",
461 self.simd_feature_enabled,
462 self.accelerated_backend_active,
463 self.unsafe_boundary_enforced,
464 self.security_posture,
465 self.wipe_posture,
466 self.ct_gate_posture,
467 )
468 }
469 }
470
471 impl BackendReport {
472 #[must_use]
482 pub const fn satisfies(self, policy: BackendPolicy) -> bool {
483 match policy {
484 BackendPolicy::ScalarExecutionOnly => {
485 matches!(self.active, Backend::Scalar) && !self.accelerated_backend_active
486 }
487 BackendPolicy::SimdFeatureDisabled => !self.simd_feature_enabled,
488 BackendPolicy::NoDetectedSimdCandidate => matches!(self.candidate, Backend::Scalar),
489 BackendPolicy::HighAssuranceScalarOnly => {
490 matches!(self.active, Backend::Scalar)
491 && matches!(self.candidate, Backend::Scalar)
492 && !self.simd_feature_enabled
493 && !self.accelerated_backend_active
494 && self.unsafe_boundary_enforced
495 }
496 }
497 }
498
499 #[must_use]
510 pub const fn candidate_required_cpu_features(self) -> &'static [&'static str] {
511 self.candidate.required_cpu_features()
512 }
513
514 #[must_use]
523 pub const fn snapshot(self) -> BackendSnapshot {
524 BackendSnapshot {
525 active: self.active.as_str(),
526 candidate: self.candidate.as_str(),
527 candidate_detection_mode: self.candidate_detection_mode.as_str(),
528 candidate_required_cpu_features: self.candidate_required_cpu_features(),
529 simd_feature_enabled: self.simd_feature_enabled,
530 accelerated_backend_active: self.accelerated_backend_active,
531 unsafe_boundary_enforced: self.unsafe_boundary_enforced,
532 security_posture: self.security_posture.as_str(),
533 wipe_posture: self.wipe_posture.as_str(),
534 ct_gate_posture: self.ct_gate_posture.as_str(),
535 }
536 }
537 }
538
539 #[must_use]
548 pub fn backend_report() -> BackendReport {
549 let active = active_backend();
550 let candidate = detected_candidate();
551 let candidate_detection_mode = candidate_detection_mode();
552 let accelerated_backend_active = active != Backend::Scalar;
553 let unsafe_boundary_enforced = !cfg!(feature = "simd");
554 let security_posture = if accelerated_backend_active {
555 SecurityPosture::Accelerated
556 } else if candidate != Backend::Scalar {
557 SecurityPosture::SimdCandidateScalarActive
558 } else {
559 SecurityPosture::ScalarOnly
560 };
561
562 BackendReport {
563 active,
564 candidate,
565 candidate_detection_mode,
566 simd_feature_enabled: cfg!(feature = "simd"),
567 accelerated_backend_active,
568 unsafe_boundary_enforced,
569 security_posture,
570 wipe_posture: wipe_posture(),
571 ct_gate_posture: ct_gate_posture(),
572 }
573 }
574
575 const fn wipe_posture() -> WipePosture {
576 if cfg!(any(
577 target_arch = "aarch64",
578 target_arch = "arm",
579 target_arch = "riscv32",
580 target_arch = "riscv64",
581 target_arch = "x86",
582 target_arch = "x86_64",
583 )) {
584 WipePosture::HardwareFence
585 } else {
586 WipePosture::CompilerFenceOnly
587 }
588 }
589
590 const fn ct_gate_posture() -> CtGatePosture {
591 if cfg!(any(
592 target_arch = "aarch64",
593 target_arch = "x86",
594 target_arch = "x86_64"
595 )) {
596 CtGatePosture::HardwareSpeculationBarrier
597 } else if cfg!(any(
598 target_arch = "arm",
599 target_arch = "riscv32",
600 target_arch = "riscv64"
601 )) {
602 CtGatePosture::OrderingFence
603 } else {
604 CtGatePosture::CompilerFenceOnly
605 }
606 }
607
608 pub fn require_backend_policy(policy: BackendPolicy) -> Result<(), BackendPolicyError> {
617 let report = backend_report();
618 if report.satisfies(policy) {
619 Ok(())
620 } else {
621 Err(BackendPolicyError { policy, report })
622 }
623 }
624
625 fn write_feature_list(
626 formatter: &mut core::fmt::Formatter<'_>,
627 features: &[&str],
628 ) -> core::fmt::Result {
629 formatter.write_str("[")?;
630 let mut index = 0;
631 while index < features.len() {
632 if index != 0 {
633 formatter.write_str(",")?;
634 }
635 formatter.write_str(features[index])?;
636 index += 1;
637 }
638 formatter.write_str("]")
639 }
640
641 #[cfg(feature = "simd")]
642 fn active_backend() -> Backend {
643 match super::simd::active_backend() {
644 super::simd::ActiveBackend::Scalar => Backend::Scalar,
645 }
646 }
647
648 #[cfg(not(feature = "simd"))]
649 const fn active_backend() -> Backend {
650 Backend::Scalar
651 }
652
653 #[cfg(feature = "simd")]
654 fn detected_candidate() -> Backend {
655 match super::simd::detected_candidate() {
656 super::simd::Candidate::Scalar => Backend::Scalar,
657 #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
658 super::simd::Candidate::Avx512Vbmi => Backend::Avx512Vbmi,
659 #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
660 super::simd::Candidate::Avx2 => Backend::Avx2,
661 #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
662 super::simd::Candidate::Ssse3Sse41 => Backend::Ssse3Sse41,
663 #[cfg(any(target_arch = "aarch64", target_arch = "arm"))]
664 super::simd::Candidate::Neon => Backend::Neon,
665 #[cfg(target_arch = "wasm32")]
666 super::simd::Candidate::WasmSimd128 => Backend::WasmSimd128,
667 }
668 }
669
670 #[cfg(not(feature = "simd"))]
671 const fn detected_candidate() -> Backend {
672 Backend::Scalar
673 }
674
675 #[cfg(all(
676 feature = "simd",
677 feature = "std",
678 any(target_arch = "x86", target_arch = "x86_64")
679 ))]
680 const fn candidate_detection_mode() -> CandidateDetectionMode {
681 CandidateDetectionMode::RuntimeCpuFeatures
682 }
683
684 #[cfg(all(
685 feature = "simd",
686 not(all(feature = "std", any(target_arch = "x86", target_arch = "x86_64")))
687 ))]
688 const fn candidate_detection_mode() -> CandidateDetectionMode {
689 CandidateDetectionMode::CompileTimeTargetFeatures
690 }
691
692 #[cfg(not(feature = "simd"))]
693 const fn candidate_detection_mode() -> CandidateDetectionMode {
694 CandidateDetectionMode::SimdFeatureDisabled
695 }
696}
697
698#[cfg(feature = "stream")]
699pub mod stream {
700 use super::{Alphabet, DecodeError, EncodeError, Engine};
735 use std::io::{self, Read, Write};
736
737 struct OutputQueue<const CAP: usize> {
738 buffer: [u8; CAP],
739 start: usize,
740 len: usize,
741 }
742
743 impl<const CAP: usize> OutputQueue<CAP> {
744 const fn new() -> Self {
745 Self {
746 buffer: [0; CAP],
747 start: 0,
748 len: 0,
749 }
750 }
751
752 const fn is_empty(&self) -> bool {
753 self.len == 0
754 }
755
756 const fn len(&self) -> usize {
757 self.len
758 }
759
760 const fn capacity(&self) -> usize {
761 self.len + self.available_capacity()
762 }
763
764 fn push_slice(&mut self, input: &[u8]) -> io::Result<()> {
765 if input.len() > self.available_capacity() {
766 return Err(io::Error::other(
767 "base64 stream output queue capacity exceeded",
768 ));
769 }
770
771 let mut read = 0;
772 while read < input.len() {
773 let write = (self.start + self.len) % CAP;
774 self.buffer[write] = input[read];
775 self.len += 1;
776 read += 1;
777 }
778
779 Ok(())
780 }
781
782 fn copy_front(&self, output: &mut [u8]) -> usize {
783 let count = core::cmp::min(self.len, output.len());
784 let first = core::cmp::min(count, CAP - self.start);
785 output[..first].copy_from_slice(&self.buffer[self.start..self.start + first]);
786
787 let second = count - first;
788 if second > 0 {
789 output[first..first + second].copy_from_slice(&self.buffer[..second]);
790 }
791
792 count
793 }
794
795 fn discard_front(&mut self, count: usize) {
796 let count = core::cmp::min(count, self.len);
797 let first = core::cmp::min(count, CAP - self.start);
798 crate::wipe_bytes(&mut self.buffer[self.start..self.start + first]);
799
800 let second = count - first;
801 if second > 0 {
802 crate::wipe_bytes(&mut self.buffer[..second]);
803 }
804
805 self.start = (self.start + count) % CAP;
806 self.len -= count;
807 if self.len == 0 {
808 self.start = 0;
809 }
810 }
811
812 fn pop_slice(&mut self, output: &mut [u8]) -> usize {
813 let count = self.copy_front(output);
814 self.discard_front(count);
815 count
816 }
817
818 fn clear_all(&mut self) {
819 crate::wipe_bytes(&mut self.buffer);
820 self.start = 0;
821 self.len = 0;
822 }
823
824 const fn available_capacity(&self) -> usize {
825 CAP - self.len
826 }
827 }
828
829 pub struct Encoder<W, A, const PAD: bool>
837 where
838 A: Alphabet,
839 {
840 inner: Option<W>,
841 engine: Engine<A, PAD>,
842 pending: [u8; 2],
843 pending_len: usize,
844 output: OutputQueue<1024>,
845 finalized: bool,
846 }
847
848 impl<W, A, const PAD: bool> Encoder<W, A, PAD>
849 where
850 A: Alphabet,
851 {
852 #[must_use]
854 pub const fn new(inner: W, engine: Engine<A, PAD>) -> Self {
855 Self {
856 inner: Some(inner),
857 engine,
858 pending: [0; 2],
859 pending_len: 0,
860 output: OutputQueue::new(),
861 finalized: false,
862 }
863 }
864
865 #[must_use]
867 pub fn get_ref(&self) -> &W {
868 self.inner_ref()
869 }
870
871 pub fn get_mut(&mut self) -> &mut W {
873 self.inner_mut()
874 }
875
876 #[must_use]
878 pub const fn engine(&self) -> Engine<A, PAD> {
879 self.engine
880 }
881
882 #[must_use]
884 pub const fn is_padded(&self) -> bool {
885 PAD
886 }
887
888 #[must_use]
891 pub const fn pending_len(&self) -> usize {
892 self.pending_len
893 }
894
895 #[must_use]
898 pub const fn has_pending_input(&self) -> bool {
899 self.pending_len != 0
900 }
901
902 #[must_use]
907 pub const fn pending_input_needed_len(&self) -> usize {
908 if self.has_pending_input() {
909 3 - self.pending_len
910 } else {
911 0
912 }
913 }
914
915 #[must_use]
918 pub const fn buffered_output_len(&self) -> usize {
919 self.output.len()
920 }
921
922 #[must_use]
925 pub const fn buffered_output_capacity(&self) -> usize {
926 self.output.capacity()
927 }
928
929 #[must_use]
932 pub const fn buffered_output_remaining_capacity(&self) -> usize {
933 self.output.available_capacity()
934 }
935
936 #[must_use]
939 pub const fn has_buffered_output(&self) -> bool {
940 !self.output.is_empty()
941 }
942
943 #[must_use]
947 pub const fn is_finalized(&self) -> bool {
948 self.finalized
949 }
950
951 #[must_use]
954 pub const fn can_into_inner(&self) -> bool {
955 !self.has_pending_input() && !self.has_buffered_output()
956 }
957
958 #[must_use]
962 pub fn into_inner(mut self) -> W {
963 self.take_inner()
964 }
965
966 #[allow(clippy::result_large_err)]
972 pub fn try_into_inner(mut self) -> Result<W, Self> {
973 if !self.can_into_inner() {
974 return Err(self);
975 }
976 Ok(self.take_inner())
977 }
978
979 fn inner_ref(&self) -> &W {
980 match &self.inner {
981 Some(inner) => inner,
982 None => unreachable!("stream encoder inner writer was already taken"),
983 }
984 }
985
986 fn inner_mut(&mut self) -> &mut W {
987 match &mut self.inner {
988 Some(inner) => inner,
989 None => unreachable!("stream encoder inner writer was already taken"),
990 }
991 }
992
993 fn take_inner(&mut self) -> W {
994 match self.inner.take() {
995 Some(inner) => inner,
996 None => unreachable!("stream encoder inner writer was already taken"),
997 }
998 }
999
1000 fn clear_pending(&mut self) {
1001 crate::wipe_bytes(&mut self.pending);
1002 self.pending_len = 0;
1003 }
1004
1005 fn clear_output(&mut self) {
1006 self.output.clear_all();
1007 }
1008 }
1009
1010 impl<W, A, const PAD: bool> Drop for Encoder<W, A, PAD>
1011 where
1012 A: Alphabet,
1013 {
1014 fn drop(&mut self) {
1015 self.clear_pending();
1016 self.clear_output();
1017 }
1018 }
1019
1020 impl<W, A, const PAD: bool> core::fmt::Debug for Encoder<W, A, PAD>
1021 where
1022 A: Alphabet,
1023 {
1024 fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
1025 formatter
1026 .debug_struct("Encoder")
1027 .field("inner", &redacted_inner_state(self.inner.is_some()))
1028 .field("engine", &self.engine)
1029 .field("pending", &"<redacted>")
1030 .field("pending_len", &self.pending_len)
1031 .field("pending_input_needed_len", &self.pending_input_needed_len())
1032 .field("buffered_output_len", &self.output.len())
1033 .field("buffered_output_capacity", &self.output.capacity())
1034 .field(
1035 "buffered_output_remaining_capacity",
1036 &self.output.available_capacity(),
1037 )
1038 .field("can_into_inner", &self.can_into_inner())
1039 .field("finalized", &self.finalized)
1040 .finish()
1041 }
1042 }
1043
1044 impl<W, A, const PAD: bool> Encoder<W, A, PAD>
1045 where
1046 W: Write,
1047 A: Alphabet,
1048 {
1049 pub fn try_finish(&mut self) -> io::Result<()> {
1059 if !self.finalized {
1060 self.queue_pending_final()?;
1061 self.finalized = true;
1062 }
1063 self.flush()
1064 }
1065
1066 pub fn finish(mut self) -> io::Result<W> {
1068 self.try_finish()?;
1069 Ok(self.take_inner())
1070 }
1071
1072 fn queue_pending_final(&mut self) -> io::Result<()> {
1073 if self.pending_len == 0 {
1074 return Ok(());
1075 }
1076
1077 let mut pending = [0u8; 2];
1078 pending[..self.pending_len].copy_from_slice(&self.pending[..self.pending_len]);
1079 let pending_len = self.pending_len;
1080 let mut encoded = [0u8; 4];
1081 let result = self.queue_encoded_temp(&pending[..pending_len], &mut encoded);
1082 crate::wipe_bytes(&mut pending);
1083 result?;
1084 self.clear_pending();
1085 Ok(())
1086 }
1087
1088 fn queue_encoded_temp(&mut self, input: &[u8], encoded: &mut [u8]) -> io::Result<()> {
1089 let written = match self.engine.encode_slice(input, encoded) {
1090 Ok(written) => written,
1091 Err(err) => {
1092 crate::wipe_bytes(encoded);
1093 return Err(encode_error_to_io(err));
1094 }
1095 };
1096
1097 let result = self.output.push_slice(&encoded[..written]);
1098 crate::wipe_bytes(encoded);
1099 result
1100 }
1101
1102 fn drain_output(&mut self) -> io::Result<()> {
1103 let mut chunk = [0u8; 1024];
1104 while !self.output.is_empty() {
1105 let pending = self.output.copy_front(&mut chunk);
1106 let result = self.inner_mut().write(&chunk[..pending]);
1107 crate::wipe_bytes(&mut chunk[..pending]);
1108 match result {
1109 Ok(0) => {
1110 return Err(io::Error::new(
1111 io::ErrorKind::WriteZero,
1112 "base64 stream encoder could not drain buffered output",
1113 ));
1114 }
1115 Ok(written) => {
1116 if written > pending {
1117 return Err(io::Error::new(
1118 io::ErrorKind::InvalidData,
1119 "wrapped writer reported more bytes than provided",
1120 ));
1121 }
1122 self.output.discard_front(written);
1123 }
1124 Err(err) => return Err(err),
1125 }
1126 }
1127
1128 Ok(())
1129 }
1130 }
1131
1132 impl<W, A, const PAD: bool> Write for Encoder<W, A, PAD>
1133 where
1134 W: Write,
1135 A: Alphabet,
1136 {
1137 fn write(&mut self, input: &[u8]) -> io::Result<usize> {
1138 if input.is_empty() {
1139 self.drain_output()?;
1140 return Ok(0);
1141 }
1142 self.drain_output()?;
1143 if self.finalized {
1144 return Err(io::Error::new(
1145 io::ErrorKind::InvalidInput,
1146 "base64 stream encoder received input after finalization",
1147 ));
1148 }
1149
1150 let mut consumed = 0;
1151 if self.pending_len > 0 {
1152 let needed = 3 - self.pending_len;
1153 if input.len() < needed {
1154 self.pending[self.pending_len..self.pending_len + input.len()]
1155 .copy_from_slice(input);
1156 self.pending_len += input.len();
1157 return Ok(input.len());
1158 }
1159
1160 let mut chunk = [0u8; 3];
1161 chunk[..self.pending_len].copy_from_slice(&self.pending[..self.pending_len]);
1162 chunk[self.pending_len..].copy_from_slice(&input[..needed]);
1163
1164 let mut encoded = [0u8; 4];
1165 let result = self.queue_encoded_temp(&chunk, &mut encoded);
1166 crate::wipe_bytes(&mut chunk);
1167 result?;
1168 self.clear_pending();
1169 consumed += needed;
1170 }
1171
1172 let remaining = &input[consumed..];
1173 let full_len = remaining.len() / 3 * 3;
1174 if full_len > 0 {
1175 let max_by_queue = self.output.available_capacity() / 4 * 3;
1176 let mut take = core::cmp::min(full_len, core::cmp::min(768, max_by_queue));
1177 take -= take % 3;
1178
1179 if take == 0 {
1180 return Ok(consumed);
1181 }
1182
1183 let mut encoded = [0u8; 1024];
1184 self.queue_encoded_temp(&remaining[..take], &mut encoded)?;
1185 consumed += take;
1186
1187 if take < full_len {
1188 return Ok(consumed);
1189 }
1190 }
1191
1192 let tail = &input[consumed..];
1193 self.pending[..tail.len()].copy_from_slice(tail);
1194 self.pending_len = tail.len();
1195 consumed += tail.len();
1196
1197 Ok(consumed)
1198 }
1199
1200 fn flush(&mut self) -> io::Result<()> {
1201 self.drain_output()?;
1202 self.inner_mut().flush()
1203 }
1204 }
1205
1206 fn encode_error_to_io(err: EncodeError) -> io::Error {
1207 io::Error::new(io::ErrorKind::InvalidInput, err)
1208 }
1209
1210 pub struct Decoder<W, A, const PAD: bool>
1226 where
1227 A: Alphabet,
1228 {
1229 inner: Option<W>,
1230 engine: Engine<A, PAD>,
1231 pending: [u8; 4],
1232 pending_len: usize,
1233 output: OutputQueue<1024>,
1234 finished: bool,
1235 failed: bool,
1236 finalized: bool,
1237 }
1238
1239 impl<W, A, const PAD: bool> Decoder<W, A, PAD>
1240 where
1241 A: Alphabet,
1242 {
1243 #[must_use]
1250 pub const fn new(inner: W, engine: Engine<A, PAD>) -> Self {
1251 Self {
1252 inner: Some(inner),
1253 engine,
1254 pending: [0; 4],
1255 pending_len: 0,
1256 output: OutputQueue::new(),
1257 finished: false,
1258 finalized: false,
1259 failed: false,
1260 }
1261 }
1262
1263 #[must_use]
1265 pub fn get_ref(&self) -> &W {
1266 self.inner_ref()
1267 }
1268
1269 pub fn get_mut(&mut self) -> &mut W {
1271 self.inner_mut()
1272 }
1273
1274 #[must_use]
1276 pub const fn engine(&self) -> Engine<A, PAD> {
1277 self.engine
1278 }
1279
1280 #[must_use]
1282 pub const fn is_padded(&self) -> bool {
1283 PAD
1284 }
1285
1286 #[must_use]
1289 pub const fn pending_len(&self) -> usize {
1290 self.pending_len
1291 }
1292
1293 #[must_use]
1296 pub const fn has_pending_input(&self) -> bool {
1297 self.pending_len != 0
1298 }
1299
1300 #[must_use]
1305 pub const fn pending_input_needed_len(&self) -> usize {
1306 if self.has_pending_input() {
1307 4 - self.pending_len
1308 } else {
1309 0
1310 }
1311 }
1312
1313 #[must_use]
1316 pub const fn buffered_output_len(&self) -> usize {
1317 self.output.len()
1318 }
1319
1320 #[must_use]
1323 pub const fn buffered_output_capacity(&self) -> usize {
1324 self.output.capacity()
1325 }
1326
1327 #[must_use]
1330 pub const fn buffered_output_remaining_capacity(&self) -> usize {
1331 self.output.available_capacity()
1332 }
1333
1334 #[must_use]
1337 pub const fn has_buffered_output(&self) -> bool {
1338 !self.output.is_empty()
1339 }
1340
1341 #[must_use]
1347 pub const fn has_terminal_padding(&self) -> bool {
1348 self.finished
1349 }
1350
1351 #[must_use]
1355 pub const fn is_finalized(&self) -> bool {
1356 self.finalized
1357 }
1358
1359 #[must_use]
1365 pub const fn is_failed(&self) -> bool {
1366 self.failed
1367 }
1368
1369 #[must_use]
1372 pub const fn can_into_inner(&self) -> bool {
1373 !self.is_failed() && !self.has_pending_input() && !self.has_buffered_output()
1374 }
1375
1376 #[must_use]
1380 pub fn into_inner(mut self) -> W {
1381 self.take_inner()
1382 }
1383
1384 #[allow(clippy::result_large_err)]
1390 pub fn try_into_inner(mut self) -> Result<W, Self> {
1391 if !self.can_into_inner() {
1392 return Err(self);
1393 }
1394 Ok(self.take_inner())
1395 }
1396
1397 fn inner_ref(&self) -> &W {
1398 match &self.inner {
1399 Some(inner) => inner,
1400 None => unreachable!("stream decoder inner writer was already taken"),
1401 }
1402 }
1403
1404 fn inner_mut(&mut self) -> &mut W {
1405 match &mut self.inner {
1406 Some(inner) => inner,
1407 None => unreachable!("stream decoder inner writer was already taken"),
1408 }
1409 }
1410
1411 fn take_inner(&mut self) -> W {
1412 match self.inner.take() {
1413 Some(inner) => inner,
1414 None => unreachable!("stream decoder inner writer was already taken"),
1415 }
1416 }
1417
1418 fn clear_pending(&mut self) {
1419 crate::wipe_bytes(&mut self.pending);
1420 self.pending_len = 0;
1421 }
1422
1423 fn clear_output(&mut self) {
1424 self.output.clear_all();
1425 }
1426 }
1427
1428 impl<W, A, const PAD: bool> Drop for Decoder<W, A, PAD>
1429 where
1430 A: Alphabet,
1431 {
1432 fn drop(&mut self) {
1433 self.clear_pending();
1434 self.clear_output();
1435 }
1436 }
1437
1438 impl<W, A, const PAD: bool> core::fmt::Debug for Decoder<W, A, PAD>
1439 where
1440 A: Alphabet,
1441 {
1442 fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
1443 formatter
1444 .debug_struct("Decoder")
1445 .field("inner", &redacted_inner_state(self.inner.is_some()))
1446 .field("engine", &self.engine)
1447 .field("pending", &"<redacted>")
1448 .field("pending_len", &self.pending_len)
1449 .field("pending_input_needed_len", &self.pending_input_needed_len())
1450 .field("buffered_output_len", &self.output.len())
1451 .field("buffered_output_capacity", &self.output.capacity())
1452 .field(
1453 "buffered_output_remaining_capacity",
1454 &self.output.available_capacity(),
1455 )
1456 .field("can_into_inner", &self.can_into_inner())
1457 .field("terminal_padding", &self.finished)
1458 .field("finalized", &self.finalized)
1459 .field("failed", &self.failed)
1460 .finish()
1461 }
1462 }
1463
1464 impl<W, A, const PAD: bool> Decoder<W, A, PAD>
1465 where
1466 W: Write,
1467 A: Alphabet,
1468 {
1469 pub fn try_finish(&mut self) -> io::Result<()> {
1479 if self.failed {
1480 return Err(stream_decoder_failed_error());
1481 }
1482 if !self.finalized {
1483 self.queue_pending_final()?;
1484 self.finalized = true;
1485 }
1486 self.flush()
1487 }
1488
1489 pub fn finish(mut self) -> io::Result<W> {
1491 self.try_finish()?;
1492 Ok(self.take_inner())
1493 }
1494
1495 fn queue_pending_final(&mut self) -> io::Result<()> {
1496 if self.pending_len == 0 {
1497 return Ok(());
1498 }
1499
1500 let mut pending = [0u8; 4];
1501 pending[..self.pending_len].copy_from_slice(&self.pending[..self.pending_len]);
1502 let pending_len = self.pending_len;
1503 let mut decoded = [0u8; 3];
1504 let result = self.queue_decoded_temp(&pending[..pending_len], &mut decoded);
1505 crate::wipe_bytes(&mut pending);
1506 if let Err(err) = result {
1507 self.clear_pending();
1508 return Err(err);
1509 }
1510 self.clear_pending();
1511 Ok(())
1512 }
1513
1514 fn queue_full_quad(&mut self, mut input: [u8; 4]) -> io::Result<()> {
1515 let mut decoded = [0u8; 3];
1516 let result = self.queue_decoded_temp(&input, &mut decoded);
1517 crate::wipe_bytes(&mut input);
1518 let written = result?;
1519 if written < 3 {
1520 self.finished = true;
1521 }
1522 Ok(())
1523 }
1524
1525 fn queue_decoded_temp(&mut self, input: &[u8], decoded: &mut [u8]) -> io::Result<usize> {
1526 let written = match self.engine.decode_slice(input, decoded) {
1527 Ok(written) => written,
1528 Err(err) => {
1529 crate::wipe_bytes(decoded);
1530 self.failed = true;
1531 return Err(decode_error_to_io(err));
1532 }
1533 };
1534
1535 let result = self.output.push_slice(&decoded[..written]);
1536 crate::wipe_bytes(decoded);
1537 result?;
1538 Ok(written)
1539 }
1540
1541 fn drain_output(&mut self) -> io::Result<()> {
1542 let mut chunk = [0u8; 1024];
1543 while !self.output.is_empty() {
1544 let pending = self.output.copy_front(&mut chunk);
1545 let result = self.inner_mut().write(&chunk[..pending]);
1546 crate::wipe_bytes(&mut chunk[..pending]);
1547 match result {
1548 Ok(0) => {
1549 return Err(io::Error::new(
1550 io::ErrorKind::WriteZero,
1551 "base64 stream decoder could not drain buffered output",
1552 ));
1553 }
1554 Ok(written) => {
1555 if written > pending {
1556 return Err(io::Error::new(
1557 io::ErrorKind::InvalidData,
1558 "wrapped writer reported more bytes than provided",
1559 ));
1560 }
1561 self.output.discard_front(written);
1562 }
1563 Err(err) => return Err(err),
1564 }
1565 }
1566
1567 Ok(())
1568 }
1569 }
1570
1571 impl<W, A, const PAD: bool> Write for Decoder<W, A, PAD>
1572 where
1573 W: Write,
1574 A: Alphabet,
1575 {
1576 fn write(&mut self, input: &[u8]) -> io::Result<usize> {
1577 if self.failed {
1578 return Err(stream_decoder_failed_error());
1579 }
1580 if input.is_empty() {
1581 self.drain_output()?;
1582 return Ok(0);
1583 }
1584 self.drain_output()?;
1585 if self.finalized {
1586 return Err(io::Error::new(
1587 io::ErrorKind::InvalidInput,
1588 "base64 stream decoder received input after finalization",
1589 ));
1590 }
1591 if self.finished {
1592 self.failed = true;
1593 return Err(trailing_input_after_padding_error());
1594 }
1595
1596 let mut consumed = 0;
1597 if self.pending_len > 0 {
1598 let needed = 4 - self.pending_len;
1599 if input.len() < needed {
1600 self.pending[self.pending_len..self.pending_len + input.len()]
1601 .copy_from_slice(input);
1602 self.pending_len += input.len();
1603 return Ok(input.len());
1604 }
1605
1606 let mut quad = [0u8; 4];
1607 quad[..self.pending_len].copy_from_slice(&self.pending[..self.pending_len]);
1608 quad[self.pending_len..].copy_from_slice(&input[..needed]);
1609 let result = self.queue_full_quad(quad);
1610 crate::wipe_bytes(&mut quad);
1611 if let Err(err) = result {
1612 self.clear_pending();
1613 return Err(err);
1614 }
1615 self.clear_pending();
1616 consumed += needed;
1617
1618 if self.finished {
1619 return Ok(consumed);
1620 }
1621 }
1622
1623 while input.len() - consumed >= 4 {
1624 if self.output.available_capacity() < 3 {
1625 return Ok(consumed);
1626 }
1627
1628 let mut quad = [
1629 input[consumed],
1630 input[consumed + 1],
1631 input[consumed + 2],
1632 input[consumed + 3],
1633 ];
1634 let mut decoded = [0u8; 3];
1635 let written = match self.engine.decode_slice(&quad, &mut decoded) {
1636 Ok(written) => written,
1637 Err(err) => {
1638 crate::wipe_bytes(&mut quad);
1639 crate::wipe_bytes(&mut decoded);
1640 self.failed = true;
1641 if consumed > 0 {
1642 return Ok(consumed);
1643 }
1644
1645 return Err(decode_error_to_io(err));
1646 }
1647 };
1648
1649 let result = self.output.push_slice(&decoded[..written]);
1650 crate::wipe_bytes(&mut quad);
1651 crate::wipe_bytes(&mut decoded);
1652 result?;
1653 consumed += 4;
1654
1655 if written < 3 {
1656 self.finished = true;
1657 return Ok(consumed);
1658 }
1659 }
1660
1661 let tail = &input[consumed..];
1662 self.pending[..tail.len()].copy_from_slice(tail);
1663 self.pending_len = tail.len();
1664 consumed += tail.len();
1665
1666 Ok(consumed)
1667 }
1668
1669 fn flush(&mut self) -> io::Result<()> {
1670 if self.failed {
1671 return Err(stream_decoder_failed_error());
1672 }
1673 self.drain_output()?;
1674 self.inner_mut().flush()
1675 }
1676 }
1677
1678 fn decode_error_to_io(err: DecodeError) -> io::Error {
1679 io::Error::new(io::ErrorKind::InvalidInput, err)
1680 }
1681
1682 fn trailing_input_after_padding_error() -> io::Error {
1683 io::Error::new(
1684 io::ErrorKind::InvalidInput,
1685 "base64 decoder received trailing input after padding",
1686 )
1687 }
1688
1689 fn stream_decoder_failed_error() -> io::Error {
1690 io::Error::new(
1691 io::ErrorKind::InvalidInput,
1692 "base64 stream decoder is failed after malformed input",
1693 )
1694 }
1695
1696 fn stream_encoder_failed_error() -> io::Error {
1697 io::Error::new(
1698 io::ErrorKind::InvalidInput,
1699 "base64 stream encoder is failed after internal error",
1700 )
1701 }
1702
1703 pub struct DecoderReader<R, A, const PAD: bool>
1718 where
1719 A: Alphabet,
1720 {
1721 inner: Option<R>,
1722 engine: Engine<A, PAD>,
1723 pending: [u8; 4],
1724 pending_len: usize,
1725 output: OutputQueue<3>,
1726 finished: bool,
1727 terminal_seen: bool,
1728 failed: bool,
1729 }
1730
1731 impl<R, A, const PAD: bool> DecoderReader<R, A, PAD>
1732 where
1733 A: Alphabet,
1734 {
1735 #[must_use]
1742 pub fn new(inner: R, engine: Engine<A, PAD>) -> Self {
1743 Self {
1744 inner: Some(inner),
1745 engine,
1746 pending: [0; 4],
1747 pending_len: 0,
1748 output: OutputQueue::new(),
1749 finished: false,
1750 terminal_seen: false,
1751 failed: false,
1752 }
1753 }
1754
1755 #[must_use]
1757 pub fn get_ref(&self) -> &R {
1758 self.inner_ref()
1759 }
1760
1761 pub fn get_mut(&mut self) -> &mut R {
1763 self.inner_mut()
1764 }
1765
1766 #[must_use]
1768 pub const fn engine(&self) -> Engine<A, PAD> {
1769 self.engine
1770 }
1771
1772 #[must_use]
1774 pub const fn is_padded(&self) -> bool {
1775 PAD
1776 }
1777
1778 #[must_use]
1781 pub const fn pending_len(&self) -> usize {
1782 self.pending_len
1783 }
1784
1785 #[must_use]
1788 pub const fn has_pending_input(&self) -> bool {
1789 self.pending_len != 0
1790 }
1791
1792 #[must_use]
1797 pub const fn pending_input_needed_len(&self) -> usize {
1798 if self.has_pending_input() {
1799 4 - self.pending_len
1800 } else {
1801 0
1802 }
1803 }
1804
1805 #[must_use]
1808 pub const fn buffered_output_len(&self) -> usize {
1809 self.output.len()
1810 }
1811
1812 #[must_use]
1815 pub const fn buffered_output_capacity(&self) -> usize {
1816 self.output.capacity()
1817 }
1818
1819 #[must_use]
1822 pub const fn buffered_output_remaining_capacity(&self) -> usize {
1823 self.output.available_capacity()
1824 }
1825
1826 #[must_use]
1829 pub const fn has_buffered_output(&self) -> bool {
1830 !self.output.is_empty()
1831 }
1832
1833 #[must_use]
1840 pub const fn has_terminal_padding(&self) -> bool {
1841 self.terminal_seen
1842 }
1843
1844 #[must_use]
1850 pub const fn has_finished_input(&self) -> bool {
1851 self.finished
1852 }
1853
1854 #[must_use]
1857 pub const fn is_finished(&self) -> bool {
1858 self.finished && self.output.is_empty()
1859 }
1860
1861 #[must_use]
1868 pub const fn is_failed(&self) -> bool {
1869 self.failed
1870 }
1871
1872 #[must_use]
1875 pub const fn can_into_inner(&self) -> bool {
1876 !self.is_failed() && self.is_finished()
1877 }
1878
1879 #[must_use]
1881 pub fn into_inner(mut self) -> R {
1882 self.take_inner()
1883 }
1884
1885 #[allow(clippy::result_large_err)]
1893 pub fn try_into_inner(mut self) -> Result<R, Self> {
1894 if !self.can_into_inner() {
1895 return Err(self);
1896 }
1897 Ok(self.take_inner())
1898 }
1899
1900 fn inner_ref(&self) -> &R {
1901 match &self.inner {
1902 Some(inner) => inner,
1903 None => unreachable!("stream decoder reader inner reader was already taken"),
1904 }
1905 }
1906
1907 fn inner_mut(&mut self) -> &mut R {
1908 match &mut self.inner {
1909 Some(inner) => inner,
1910 None => unreachable!("stream decoder reader inner reader was already taken"),
1911 }
1912 }
1913
1914 fn take_inner(&mut self) -> R {
1915 match self.inner.take() {
1916 Some(inner) => inner,
1917 None => unreachable!("stream decoder reader inner reader was already taken"),
1918 }
1919 }
1920
1921 fn clear_pending(&mut self) {
1922 crate::wipe_bytes(&mut self.pending);
1923 self.pending_len = 0;
1924 }
1925 }
1926
1927 impl<R, A, const PAD: bool> Drop for DecoderReader<R, A, PAD>
1928 where
1929 A: Alphabet,
1930 {
1931 fn drop(&mut self) {
1932 self.clear_pending();
1933 self.output.clear_all();
1934 }
1935 }
1936
1937 impl<R, A, const PAD: bool> core::fmt::Debug for DecoderReader<R, A, PAD>
1938 where
1939 A: Alphabet,
1940 {
1941 fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
1942 formatter
1943 .debug_struct("DecoderReader")
1944 .field("inner", &redacted_inner_state(self.inner.is_some()))
1945 .field("engine", &self.engine)
1946 .field("pending", &"<redacted>")
1947 .field("pending_len", &self.pending_len)
1948 .field("pending_input_needed_len", &self.pending_input_needed_len())
1949 .field("buffered_output_len", &self.output.len())
1950 .field("buffered_output_capacity", &self.output.capacity())
1951 .field(
1952 "buffered_output_remaining_capacity",
1953 &self.output.available_capacity(),
1954 )
1955 .field("can_into_inner", &self.can_into_inner())
1956 .field("finished", &self.finished)
1957 .field("terminal_padding", &self.terminal_seen)
1958 .field("failed", &self.failed)
1959 .finish()
1960 }
1961 }
1962
1963 impl<R, A, const PAD: bool> Read for DecoderReader<R, A, PAD>
1964 where
1965 R: Read,
1966 A: Alphabet,
1967 {
1968 fn read(&mut self, output: &mut [u8]) -> io::Result<usize> {
1969 if output.is_empty() {
1970 return Ok(0);
1971 }
1972 if self.failed {
1973 return Err(stream_decoder_failed_error());
1974 }
1975
1976 while self.output.is_empty() && !self.finished {
1977 self.fill_output()?;
1978 }
1979
1980 Ok(self.output.pop_slice(output))
1981 }
1982 }
1983
1984 impl<R, A, const PAD: bool> DecoderReader<R, A, PAD>
1985 where
1986 R: Read,
1987 A: Alphabet,
1988 {
1989 fn fill_output(&mut self) -> io::Result<()> {
1990 if self.failed {
1991 return Err(stream_decoder_failed_error());
1992 }
1993 if self.terminal_seen {
1994 self.finished = true;
1995 return Ok(());
1996 }
1997
1998 let mut input = [0u8; 4];
1999 let available = 4 - self.pending_len;
2000 let read = match self.inner_mut().read(&mut input[..available]) {
2001 Ok(read) => read,
2002 Err(err) => {
2003 crate::wipe_bytes(&mut input);
2004 return Err(err);
2005 }
2006 };
2007 if read == 0 {
2008 crate::wipe_bytes(&mut input);
2009 self.finished = true;
2010 self.push_final_pending()?;
2011 return Ok(());
2012 }
2013
2014 self.pending[self.pending_len..self.pending_len + read].copy_from_slice(&input[..read]);
2015 crate::wipe_bytes(&mut input);
2016 self.pending_len += read;
2017 if self.pending_len < 4 {
2018 return Ok(());
2019 }
2020
2021 let mut quad = self.pending;
2022 self.clear_pending();
2023 let result = self.push_decoded(&quad);
2024 crate::wipe_bytes(&mut quad);
2025 result?;
2026 if self.terminal_seen {
2027 self.finished = true;
2028 }
2029 Ok(())
2030 }
2031
2032 fn push_final_pending(&mut self) -> io::Result<()> {
2033 if self.pending_len == 0 {
2034 return Ok(());
2035 }
2036
2037 let mut pending = [0u8; 4];
2038 pending[..self.pending_len].copy_from_slice(&self.pending[..self.pending_len]);
2039 let pending_len = self.pending_len;
2040 self.clear_pending();
2041 let result = self.push_decoded(&pending[..pending_len]);
2042 crate::wipe_bytes(&mut pending);
2043 result
2044 }
2045
2046 fn push_decoded(&mut self, input: &[u8]) -> io::Result<()> {
2047 let mut decoded = [0u8; 3];
2048 let written = match self.engine.decode_slice(input, &mut decoded) {
2049 Ok(written) => written,
2050 Err(err) => {
2051 crate::wipe_bytes(&mut decoded);
2052 self.failed = true;
2053 return Err(decode_error_to_io(err));
2054 }
2055 };
2056 let result = self.output.push_slice(&decoded[..written]);
2057 crate::wipe_bytes(&mut decoded);
2058 result?;
2059 if input.len() == 4 && written < 3 {
2060 self.terminal_seen = true;
2061 }
2062 Ok(())
2063 }
2064 }
2065
2066 pub struct EncoderReader<R, A, const PAD: bool>
2068 where
2069 A: Alphabet,
2070 {
2071 inner: Option<R>,
2072 engine: Engine<A, PAD>,
2073 pending: [u8; 2],
2074 pending_len: usize,
2075 output: OutputQueue<1024>,
2076 finished: bool,
2077 failed: bool,
2078 }
2079
2080 impl<R, A, const PAD: bool> EncoderReader<R, A, PAD>
2081 where
2082 A: Alphabet,
2083 {
2084 #[must_use]
2086 pub fn new(inner: R, engine: Engine<A, PAD>) -> Self {
2087 Self {
2088 inner: Some(inner),
2089 engine,
2090 pending: [0; 2],
2091 pending_len: 0,
2092 output: OutputQueue::new(),
2093 finished: false,
2094 failed: false,
2095 }
2096 }
2097
2098 #[must_use]
2100 pub fn get_ref(&self) -> &R {
2101 self.inner_ref()
2102 }
2103
2104 pub fn get_mut(&mut self) -> &mut R {
2106 self.inner_mut()
2107 }
2108
2109 #[must_use]
2111 pub const fn engine(&self) -> Engine<A, PAD> {
2112 self.engine
2113 }
2114
2115 #[must_use]
2117 pub const fn is_padded(&self) -> bool {
2118 PAD
2119 }
2120
2121 #[must_use]
2124 pub const fn pending_len(&self) -> usize {
2125 self.pending_len
2126 }
2127
2128 #[must_use]
2131 pub const fn has_pending_input(&self) -> bool {
2132 self.pending_len != 0
2133 }
2134
2135 #[must_use]
2140 pub const fn pending_input_needed_len(&self) -> usize {
2141 if self.has_pending_input() {
2142 3 - self.pending_len
2143 } else {
2144 0
2145 }
2146 }
2147
2148 #[must_use]
2151 pub const fn buffered_output_len(&self) -> usize {
2152 self.output.len()
2153 }
2154
2155 #[must_use]
2158 pub const fn buffered_output_capacity(&self) -> usize {
2159 self.output.capacity()
2160 }
2161
2162 #[must_use]
2165 pub const fn buffered_output_remaining_capacity(&self) -> usize {
2166 self.output.available_capacity()
2167 }
2168
2169 #[must_use]
2172 pub const fn has_buffered_output(&self) -> bool {
2173 !self.output.is_empty()
2174 }
2175
2176 #[must_use]
2182 pub const fn has_finished_input(&self) -> bool {
2183 self.finished
2184 }
2185
2186 #[must_use]
2189 pub const fn is_finished(&self) -> bool {
2190 self.finished && self.output.is_empty()
2191 }
2192
2193 #[must_use]
2196 pub const fn is_failed(&self) -> bool {
2197 self.failed
2198 }
2199
2200 #[must_use]
2203 pub const fn can_into_inner(&self) -> bool {
2204 self.is_finished() && !self.failed
2205 }
2206
2207 #[must_use]
2209 pub fn into_inner(mut self) -> R {
2210 self.take_inner()
2211 }
2212
2213 #[allow(clippy::result_large_err)]
2220 pub fn try_into_inner(mut self) -> Result<R, Self> {
2221 if !self.can_into_inner() {
2222 return Err(self);
2223 }
2224 Ok(self.take_inner())
2225 }
2226
2227 fn inner_ref(&self) -> &R {
2228 match &self.inner {
2229 Some(inner) => inner,
2230 None => unreachable!("stream encoder reader inner reader was already taken"),
2231 }
2232 }
2233
2234 fn inner_mut(&mut self) -> &mut R {
2235 match &mut self.inner {
2236 Some(inner) => inner,
2237 None => unreachable!("stream encoder reader inner reader was already taken"),
2238 }
2239 }
2240
2241 fn take_inner(&mut self) -> R {
2242 match self.inner.take() {
2243 Some(inner) => inner,
2244 None => unreachable!("stream encoder reader inner reader was already taken"),
2245 }
2246 }
2247
2248 fn clear_pending(&mut self) {
2249 crate::wipe_bytes(&mut self.pending);
2250 self.pending_len = 0;
2251 }
2252 }
2253
2254 impl<R, A, const PAD: bool> Drop for EncoderReader<R, A, PAD>
2255 where
2256 A: Alphabet,
2257 {
2258 fn drop(&mut self) {
2259 self.clear_pending();
2260 self.output.clear_all();
2261 }
2262 }
2263
2264 impl<R, A, const PAD: bool> core::fmt::Debug for EncoderReader<R, A, PAD>
2265 where
2266 A: Alphabet,
2267 {
2268 fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
2269 formatter
2270 .debug_struct("EncoderReader")
2271 .field("inner", &redacted_inner_state(self.inner.is_some()))
2272 .field("engine", &self.engine)
2273 .field("pending", &"<redacted>")
2274 .field("pending_len", &self.pending_len)
2275 .field("pending_input_needed_len", &self.pending_input_needed_len())
2276 .field("buffered_output_len", &self.output.len())
2277 .field("buffered_output_capacity", &self.output.capacity())
2278 .field(
2279 "buffered_output_remaining_capacity",
2280 &self.output.available_capacity(),
2281 )
2282 .field("can_into_inner", &self.can_into_inner())
2283 .field("finished", &self.finished)
2284 .field("failed", &self.failed)
2285 .finish()
2286 }
2287 }
2288
2289 impl<R, A, const PAD: bool> Read for EncoderReader<R, A, PAD>
2290 where
2291 R: Read,
2292 A: Alphabet,
2293 {
2294 fn read(&mut self, output: &mut [u8]) -> io::Result<usize> {
2295 if self.failed {
2296 return Err(stream_encoder_failed_error());
2297 }
2298
2299 if output.is_empty() {
2300 return Ok(0);
2301 }
2302
2303 while self.output.is_empty() && !self.finished {
2304 self.fill_output()?;
2305 }
2306
2307 Ok(self.output.pop_slice(output))
2308 }
2309 }
2310
2311 impl<R, A, const PAD: bool> EncoderReader<R, A, PAD>
2312 where
2313 R: Read,
2314 A: Alphabet,
2315 {
2316 fn fill_output(&mut self) -> io::Result<()> {
2317 let mut input = [0u8; 768];
2318 let read = match self.inner_mut().read(&mut input) {
2319 Ok(read) => read,
2320 Err(err) => {
2321 crate::wipe_bytes(&mut input);
2322 return Err(err);
2323 }
2324 };
2325 if read == 0 {
2326 crate::wipe_bytes(&mut input);
2327 self.finished = true;
2328 if let Err(err) = self.push_final_pending() {
2329 self.failed = true;
2330 return Err(err);
2331 }
2332 return Ok(());
2333 }
2334
2335 let mut consumed = 0;
2336 if self.pending_len > 0 {
2337 let needed = 3 - self.pending_len;
2338 if read < needed {
2339 self.pending[self.pending_len..self.pending_len + read]
2340 .copy_from_slice(&input[..read]);
2341 self.pending_len += read;
2342 crate::wipe_bytes(&mut input);
2343 return Ok(());
2344 }
2345
2346 let mut chunk = [0u8; 3];
2347 chunk[..self.pending_len].copy_from_slice(&self.pending[..self.pending_len]);
2348 chunk[self.pending_len..].copy_from_slice(&input[..needed]);
2349 let result = self.push_encoded(&chunk);
2350 crate::wipe_bytes(&mut chunk);
2351 if let Err(err) = result {
2352 crate::wipe_bytes(&mut input);
2353 self.failed = true;
2354 return Err(err);
2355 }
2356 self.clear_pending();
2357 consumed += needed;
2358 }
2359
2360 let remaining = &input[consumed..read];
2361 let full_len = remaining.len() / 3 * 3;
2362 let tail_len = remaining.len() - full_len;
2363 let mut tail = [0u8; 2];
2364 tail[..tail_len].copy_from_slice(&remaining[full_len..]);
2365 let result = if full_len > 0 {
2366 self.push_encoded(&remaining[..full_len])
2367 } else {
2368 Ok(())
2369 };
2370 crate::wipe_bytes(&mut input);
2371 if let Err(err) = result {
2372 crate::wipe_bytes(&mut tail);
2373 self.failed = true;
2374 return Err(err);
2375 }
2376 self.pending[..tail_len].copy_from_slice(&tail[..tail_len]);
2377 crate::wipe_bytes(&mut tail);
2378 self.pending_len = tail_len;
2379 Ok(())
2380 }
2381
2382 fn push_final_pending(&mut self) -> io::Result<()> {
2383 if self.pending_len == 0 {
2384 return Ok(());
2385 }
2386
2387 let mut pending = [0u8; 2];
2388 pending[..self.pending_len].copy_from_slice(&self.pending[..self.pending_len]);
2389 let pending_len = self.pending_len;
2390 self.clear_pending();
2391 let result = self.push_encoded(&pending[..pending_len]);
2392 crate::wipe_bytes(&mut pending);
2393 result
2394 }
2395
2396 fn push_encoded(&mut self, input: &[u8]) -> io::Result<()> {
2397 let mut encoded = [0u8; 1024];
2398 let written = match self.engine.encode_slice(input, &mut encoded) {
2399 Ok(written) => written,
2400 Err(err) => {
2401 crate::wipe_bytes(&mut encoded);
2402 return Err(encode_error_to_io(err));
2403 }
2404 };
2405 let result = self.output.push_slice(&encoded[..written]);
2406 crate::wipe_bytes(&mut encoded);
2407 result
2408 }
2409 }
2410
2411 const fn redacted_inner_state(present: bool) -> &'static str {
2412 if present { "<present>" } else { "<taken>" }
2413 }
2414}
2415
2416pub mod ct {
2458 use super::{
2459 Alphabet, DecodeError, DecodedBuffer, Standard, UrlSafe, ct_decode_in_place,
2460 ct_decode_slice, ct_decode_slice_staged_clear_tail, ct_decoded_len, ct_validate_decode,
2461 };
2462 use core::marker::PhantomData;
2463
2464 pub const STANDARD: CtEngine<Standard, true> = CtEngine::new();
2466
2467 pub const STANDARD_NO_PAD: CtEngine<Standard, false> = CtEngine::new();
2469
2470 pub const URL_SAFE: CtEngine<UrlSafe, true> = CtEngine::new();
2472
2473 pub const URL_SAFE_NO_PAD: CtEngine<UrlSafe, false> = CtEngine::new();
2475
2476 #[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
2478 pub struct CtEngine<A, const PAD: bool> {
2479 alphabet: PhantomData<A>,
2480 }
2481
2482 impl<A, const PAD: bool> CtEngine<A, PAD>
2483 where
2484 A: Alphabet,
2485 {
2486 #[must_use]
2488 pub const fn new() -> Self {
2489 Self {
2490 alphabet: PhantomData,
2491 }
2492 }
2493
2494 #[must_use]
2497 pub const fn is_padded(&self) -> bool {
2498 PAD
2499 }
2500
2501 pub fn validate_result(&self, input: &[u8]) -> Result<(), DecodeError> {
2517 ct_validate_decode::<A, PAD>(input)
2518 }
2519
2520 #[must_use]
2534 pub fn validate(&self, input: &[u8]) -> bool {
2535 self.validate_result(input).is_ok()
2536 }
2537
2538 pub fn decoded_len(&self, input: &[u8]) -> Result<usize, DecodeError> {
2544 ct_decoded_len::<A, PAD>(input)
2545 }
2546
2547 #[must_use = "handle decode errors; use decode_slice_staged_clear_tail for shared-memory or HSM-style threat models"]
2581 pub fn decode_slice_clear_tail(
2582 &self,
2583 input: &[u8],
2584 output: &mut [u8],
2585 ) -> Result<usize, DecodeError> {
2586 let written = match ct_decode_slice::<A, PAD>(input, output) {
2587 Ok(written) => written,
2588 Err(err) => {
2589 crate::wipe_bytes(output);
2590 return Err(err);
2591 }
2592 };
2593 crate::wipe_tail(output, written);
2594 Ok(written)
2595 }
2596
2597 pub fn decode_slice_staged_clear_tail(
2611 &self,
2612 input: &[u8],
2613 output: &mut [u8],
2614 staging: &mut [u8],
2615 ) -> Result<usize, DecodeError> {
2616 ct_decode_slice_staged_clear_tail::<A, PAD>(input, output, staging)
2617 }
2618
2619 pub fn decode_buffer<const CAP: usize>(
2635 &self,
2636 input: &[u8],
2637 ) -> Result<DecodedBuffer<CAP>, DecodeError> {
2638 let mut output = DecodedBuffer::new();
2639 let written = match self.decode_slice_clear_tail(input, &mut output.bytes) {
2640 Ok(written) => written,
2641 Err(err) => {
2642 output.clear();
2643 return Err(err);
2644 }
2645 };
2646 output.len = written;
2647 Ok(output)
2648 }
2649
2650 pub fn decode_in_place_clear_tail<'a>(
2678 &self,
2679 buffer: &'a mut [u8],
2680 ) -> Result<&'a mut [u8], DecodeError> {
2681 let len = match ct_decode_in_place::<A, PAD>(buffer) {
2682 Ok(len) => len,
2683 Err(err) => {
2684 crate::wipe_bytes(buffer);
2685 return Err(err);
2686 }
2687 };
2688 crate::wipe_tail(buffer, len);
2689 Ok(&mut buffer[..len])
2690 }
2691 }
2692
2693 impl<A, const PAD: bool> core::fmt::Display for CtEngine<A, PAD> {
2694 fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
2695 write!(formatter, "ct padded={PAD}")
2696 }
2697 }
2698}
2699
2700#[doc(alias = "ct")]
2706#[doc(alias = "constant_time")]
2707#[doc(alias = "sensitive")]
2708pub const STANDARD: Engine<Standard, true> = Engine::new();
2709
2710#[doc(alias = "ct")]
2717#[doc(alias = "constant_time")]
2718#[doc(alias = "sensitive")]
2719pub const STANDARD_NO_PAD: Engine<Standard, false> = Engine::new();
2720
2721#[doc(alias = "ct")]
2727#[doc(alias = "constant_time")]
2728#[doc(alias = "sensitive")]
2729pub const URL_SAFE: Engine<UrlSafe, true> = Engine::new();
2730
2731#[doc(alias = "ct")]
2738#[doc(alias = "constant_time")]
2739#[doc(alias = "sensitive")]
2740pub const URL_SAFE_NO_PAD: Engine<UrlSafe, false> = Engine::new();
2741
2742#[doc(alias = "ct")]
2750#[doc(alias = "constant_time")]
2751#[doc(alias = "sensitive")]
2752pub const BCRYPT_NO_PAD: Engine<Bcrypt, false> = Engine::new();
2753
2754#[doc(alias = "ct")]
2762#[doc(alias = "constant_time")]
2763#[doc(alias = "sensitive")]
2764pub const CRYPT_NO_PAD: Engine<Crypt, false> = Engine::new();
2765
2766#[derive(Clone, Copy, Debug, Eq, PartialEq)]
2768pub enum LineEnding {
2769 Lf,
2771 CrLf,
2773}
2774
2775impl LineEnding {
2776 #[must_use]
2778 pub const fn name(self) -> &'static str {
2779 match self {
2780 Self::Lf => "LF",
2781 Self::CrLf => "CRLF",
2782 }
2783 }
2784
2785 #[must_use]
2787 pub const fn as_str(self) -> &'static str {
2788 match self {
2789 Self::Lf => "\n",
2790 Self::CrLf => "\r\n",
2791 }
2792 }
2793
2794 #[must_use]
2796 pub const fn as_bytes(self) -> &'static [u8] {
2797 self.as_str().as_bytes()
2798 }
2799
2800 #[must_use]
2802 pub const fn byte_len(self) -> usize {
2803 match self {
2804 Self::Lf => 1,
2805 Self::CrLf => 2,
2806 }
2807 }
2808}
2809
2810impl core::fmt::Display for LineEnding {
2811 fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
2812 formatter.write_str(self.name())
2813 }
2814}
2815
2816#[derive(Clone, Copy, Debug, Eq, PartialEq)]
2822pub struct LineWrap {
2823 pub line_len: usize,
2825 pub line_ending: LineEnding,
2827}
2828
2829impl LineWrap {
2830 pub const MIME: Self = Self::new(76, LineEnding::CrLf);
2832 pub const PEM: Self = Self::new(64, LineEnding::Lf);
2834 pub const PEM_CRLF: Self = Self::new(64, LineEnding::CrLf);
2836
2837 #[must_use]
2850 pub const fn new(line_len: usize, line_ending: LineEnding) -> Self {
2851 assert!(line_len != 0, "base64 line wrap length must be non-zero");
2852 Self {
2853 line_len,
2854 line_ending,
2855 }
2856 }
2857
2858 #[must_use]
2865 pub const fn checked_new(line_len: usize, line_ending: LineEnding) -> Option<Self> {
2866 if line_len == 0 {
2867 None
2868 } else {
2869 Some(Self::new(line_len, line_ending))
2870 }
2871 }
2872
2873 #[must_use]
2875 pub const fn line_len(self) -> usize {
2876 self.line_len
2877 }
2878
2879 #[must_use]
2881 pub const fn line_ending(self) -> LineEnding {
2882 self.line_ending
2883 }
2884
2885 #[must_use]
2887 pub const fn is_valid(self) -> bool {
2888 self.line_len != 0
2889 }
2890}
2891
2892impl core::fmt::Display for LineWrap {
2893 fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
2894 write!(formatter, "{}:{}", self.line_len, self.line_ending.name())
2895 }
2896}
2897
2898#[inline(never)]
2899#[allow(unsafe_code)]
2900fn wipe_bytes(bytes: &mut [u8]) {
2901 for byte in bytes.iter_mut() {
2902 unsafe {
2906 core::ptr::write_volatile(byte, 0);
2907 }
2908 }
2909 wipe_barrier(bytes.as_mut_ptr(), bytes.len());
2910}
2911
2912#[inline(never)]
2913#[allow(unsafe_code)]
2914fn wipe_barrier(ptr: *mut u8, len: usize) {
2915 let _ = (ptr, len);
2916
2917 #[cfg(all(not(miri), any(target_arch = "x86", target_arch = "x86_64")))]
2918 {
2919 unsafe {
2923 core::arch::asm!(
2924 "mfence",
2925 "/* {0} {1} */",
2926 in(reg) ptr,
2927 in(reg) len,
2928 options(nostack, preserves_flags)
2929 );
2930 }
2931 }
2932
2933 #[cfg(all(not(miri), target_arch = "aarch64"))]
2934 {
2935 unsafe {
2939 core::arch::asm!(
2940 "dsb sy",
2941 "isb sy",
2942 "hint #20",
2943 "/* {0} {1} */",
2944 in(reg) ptr,
2945 in(reg) len,
2946 options(nostack, preserves_flags)
2947 );
2948 }
2949 }
2950
2951 #[cfg(all(not(miri), target_arch = "arm"))]
2952 {
2953 unsafe {
2957 core::arch::asm!(
2958 "dsb sy",
2959 "isb sy",
2960 "/* {0} {1} */",
2961 in(reg) ptr,
2962 in(reg) len,
2963 options(nostack, preserves_flags)
2964 );
2965 }
2966 }
2967
2968 #[cfg(all(not(miri), any(target_arch = "riscv32", target_arch = "riscv64")))]
2969 {
2970 unsafe {
2973 core::arch::asm!(
2974 "fence rw, rw",
2975 "/* {0} {1} */",
2976 in(reg) ptr,
2977 in(reg) len,
2978 options(nostack, preserves_flags)
2979 );
2980 }
2981 }
2982
2983 core::sync::atomic::compiler_fence(core::sync::atomic::Ordering::SeqCst);
2984}
2985
2986fn wipe_tail(bytes: &mut [u8], start: usize) {
2987 wipe_bytes(&mut bytes[start..]);
2988}
2989
2990#[cfg(feature = "alloc")]
2991#[allow(unsafe_code)]
2992fn wipe_vec_spare_capacity(bytes: &mut alloc::vec::Vec<u8>) {
2993 let ptr = bytes.as_mut_ptr();
2994 let len = bytes.len();
2995 let capacity = bytes.capacity();
2996 let spare = capacity - len;
2997 if spare == 0 {
2998 return;
2999 }
3000
3001 let mut offset = len;
3002 while offset < capacity {
3003 unsafe {
3007 core::ptr::write_volatile(ptr.add(offset), 0);
3008 }
3009 offset += 1;
3010 }
3011 let spare_ptr = unsafe { ptr.add(len) };
3014 wipe_barrier(spare_ptr, spare);
3015}
3016
3017#[cfg(feature = "alloc")]
3018fn wipe_vec_all(bytes: &mut alloc::vec::Vec<u8>) {
3019 wipe_bytes(bytes);
3020 wipe_vec_spare_capacity(bytes);
3021}
3022
3023pub struct EncodedBuffer<const CAP: usize> {
3040 bytes: [u8; CAP],
3041 len: usize,
3042}
3043
3044pub struct ExposedEncodedArray<const CAP: usize> {
3051 bytes: [u8; CAP],
3052 len: usize,
3053}
3054
3055impl<const CAP: usize> ExposedEncodedArray<CAP> {
3056 #[must_use]
3062 pub const fn from_array(bytes: [u8; CAP], len: usize) -> Self {
3063 assert!(len <= CAP, "visible length exceeds array capacity");
3064 Self { bytes, len }
3065 }
3066
3067 #[must_use]
3069 pub fn as_bytes(&self) -> &[u8] {
3070 &self.bytes[..self.len]
3071 }
3072
3073 #[must_use]
3075 pub const fn len(&self) -> usize {
3076 self.len
3077 }
3078
3079 #[must_use]
3081 pub const fn is_empty(&self) -> bool {
3082 self.len == 0
3083 }
3084
3085 #[must_use]
3087 pub const fn capacity(&self) -> usize {
3088 CAP
3089 }
3090
3091 #[must_use = "caller must zeroize the returned array"]
3097 pub fn into_exposed_unprotected_array_caller_must_zeroize(mut self) -> ([u8; CAP], usize) {
3098 let len = self.len;
3099 self.len = 0;
3100 (core::mem::replace(&mut self.bytes, [0u8; CAP]), len)
3101 }
3102}
3103
3104impl<const CAP: usize> Drop for ExposedEncodedArray<CAP> {
3105 fn drop(&mut self) {
3106 wipe_bytes(&mut self.bytes);
3107 self.len = 0;
3108 }
3109}
3110
3111impl<const CAP: usize> core::fmt::Debug for ExposedEncodedArray<CAP> {
3112 fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
3113 formatter
3114 .debug_struct("ExposedEncodedArray")
3115 .field("bytes", &"<redacted>")
3116 .field("len", &self.len)
3117 .field("capacity", &CAP)
3118 .finish()
3119 }
3120}
3121
3122impl<const CAP: usize> EncodedBuffer<CAP> {
3123 #[must_use]
3125 pub const fn new() -> Self {
3126 Self {
3127 bytes: [0u8; CAP],
3128 len: 0,
3129 }
3130 }
3131
3132 #[must_use]
3134 pub const fn len(&self) -> usize {
3135 self.len
3136 }
3137
3138 #[must_use]
3140 pub const fn is_empty(&self) -> bool {
3141 self.len == 0
3142 }
3143
3144 #[must_use]
3146 pub const fn is_full(&self) -> bool {
3147 self.len == CAP
3148 }
3149
3150 #[must_use]
3152 pub const fn capacity(&self) -> usize {
3153 CAP
3154 }
3155
3156 #[must_use]
3158 pub const fn remaining_capacity(&self) -> usize {
3159 CAP - self.len
3160 }
3161
3162 #[must_use]
3164 pub fn as_bytes(&self) -> &[u8] {
3165 &self.bytes[..self.len]
3166 }
3167
3168 pub fn as_utf8(&self) -> Result<&str, core::str::Utf8Error> {
3175 core::str::from_utf8(self.as_bytes())
3176 }
3177
3178 #[must_use]
3185 pub fn as_str(&self) -> &str {
3186 match self.as_utf8() {
3187 Ok(output) => output,
3188 Err(_) => unreachable!("base64 encoder produced non-UTF-8 output"),
3189 }
3190 }
3191
3192 #[doc(alias = "constant_time_eq")]
3211 #[must_use]
3212 pub fn constant_time_eq_public_len(&self, other: &[u8]) -> bool {
3213 constant_time_eq_public_len(self.as_bytes(), other)
3214 }
3215
3216 #[must_use]
3224 pub fn into_exposed_array(mut self) -> ExposedEncodedArray<CAP> {
3225 let len = self.len;
3226 self.len = 0;
3227 ExposedEncodedArray::from_array(core::mem::replace(&mut self.bytes, [0u8; CAP]), len)
3228 }
3229
3230 pub fn clear(&mut self) {
3232 wipe_bytes(&mut self.bytes);
3233 self.len = 0;
3234 }
3235
3236 pub fn clear_tail(&mut self) {
3238 wipe_tail(&mut self.bytes, self.len);
3239 }
3240}
3241
3242impl<const CAP: usize> AsRef<[u8]> for EncodedBuffer<CAP> {
3243 fn as_ref(&self) -> &[u8] {
3244 self.as_bytes()
3245 }
3246}
3247
3248impl<const CAP: usize> Clone for EncodedBuffer<CAP> {
3249 fn clone(&self) -> Self {
3259 let mut output = Self::new();
3260 output.bytes[..self.len].copy_from_slice(self.as_bytes());
3261 output.len = self.len;
3262 output
3263 }
3264}
3265
3266impl<const CAP: usize> core::fmt::Debug for EncodedBuffer<CAP> {
3267 fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
3268 formatter
3269 .debug_struct("EncodedBuffer")
3270 .field("bytes", &"<redacted>")
3271 .field("len", &self.len)
3272 .field("capacity", &CAP)
3273 .finish()
3274 }
3275}
3276
3277impl<const CAP: usize> core::fmt::Display for EncodedBuffer<CAP> {
3278 fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
3284 formatter.write_str(self.as_str())
3285 }
3286}
3287
3288impl<const CAP: usize> Default for EncodedBuffer<CAP> {
3289 fn default() -> Self {
3290 Self::new()
3291 }
3292}
3293
3294impl<const CAP: usize> Drop for EncodedBuffer<CAP> {
3295 fn drop(&mut self) {
3296 self.clear();
3297 }
3298}
3299
3300impl<const CAP: usize> TryFrom<&[u8]> for EncodedBuffer<CAP> {
3301 type Error = EncodeError;
3302
3303 fn try_from(input: &[u8]) -> Result<Self, Self::Error> {
3309 STANDARD.encode_buffer(input)
3310 }
3311}
3312
3313impl<const CAP: usize, const N: usize> TryFrom<&[u8; N]> for EncodedBuffer<CAP> {
3314 type Error = EncodeError;
3315
3316 fn try_from(input: &[u8; N]) -> Result<Self, Self::Error> {
3322 Self::try_from(&input[..])
3323 }
3324}
3325
3326impl<const CAP: usize> TryFrom<&str> for EncodedBuffer<CAP> {
3327 type Error = EncodeError;
3328
3329 fn try_from(input: &str) -> Result<Self, Self::Error> {
3336 Self::try_from(input.as_bytes())
3337 }
3338}
3339
3340pub struct DecodedBuffer<const CAP: usize> {
3357 bytes: [u8; CAP],
3358 len: usize,
3359}
3360
3361pub struct ExposedDecodedArray<const CAP: usize> {
3368 bytes: [u8; CAP],
3369 len: usize,
3370}
3371
3372impl<const CAP: usize> ExposedDecodedArray<CAP> {
3373 #[must_use]
3379 pub const fn from_array(bytes: [u8; CAP], len: usize) -> Self {
3380 assert!(len <= CAP, "visible length exceeds array capacity");
3381 Self { bytes, len }
3382 }
3383
3384 #[must_use]
3386 pub fn as_bytes(&self) -> &[u8] {
3387 &self.bytes[..self.len]
3388 }
3389
3390 #[must_use]
3392 pub const fn len(&self) -> usize {
3393 self.len
3394 }
3395
3396 #[must_use]
3398 pub const fn is_empty(&self) -> bool {
3399 self.len == 0
3400 }
3401
3402 #[must_use]
3404 pub const fn capacity(&self) -> usize {
3405 CAP
3406 }
3407
3408 #[must_use = "caller must zeroize the returned array"]
3414 pub fn into_exposed_unprotected_array_caller_must_zeroize(mut self) -> ([u8; CAP], usize) {
3415 let len = self.len;
3416 self.len = 0;
3417 (core::mem::replace(&mut self.bytes, [0u8; CAP]), len)
3418 }
3419}
3420
3421impl<const CAP: usize> Drop for ExposedDecodedArray<CAP> {
3422 fn drop(&mut self) {
3423 wipe_bytes(&mut self.bytes);
3424 self.len = 0;
3425 }
3426}
3427
3428impl<const CAP: usize> core::fmt::Debug for ExposedDecodedArray<CAP> {
3429 fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
3430 formatter
3431 .debug_struct("ExposedDecodedArray")
3432 .field("bytes", &"<redacted>")
3433 .field("len", &self.len)
3434 .field("capacity", &CAP)
3435 .finish()
3436 }
3437}
3438
3439impl<const CAP: usize> DecodedBuffer<CAP> {
3440 #[must_use]
3442 pub const fn new() -> Self {
3443 Self {
3444 bytes: [0u8; CAP],
3445 len: 0,
3446 }
3447 }
3448
3449 #[must_use]
3451 pub const fn len(&self) -> usize {
3452 self.len
3453 }
3454
3455 #[must_use]
3457 pub const fn is_empty(&self) -> bool {
3458 self.len == 0
3459 }
3460
3461 #[must_use]
3463 pub const fn is_full(&self) -> bool {
3464 self.len == CAP
3465 }
3466
3467 #[must_use]
3469 pub const fn capacity(&self) -> usize {
3470 CAP
3471 }
3472
3473 #[must_use]
3475 pub const fn remaining_capacity(&self) -> usize {
3476 CAP - self.len
3477 }
3478
3479 #[must_use]
3481 pub fn as_bytes(&self) -> &[u8] {
3482 &self.bytes[..self.len]
3483 }
3484
3485 pub fn as_utf8(&self) -> Result<&str, core::str::Utf8Error> {
3491 core::str::from_utf8(self.as_bytes())
3492 }
3493
3494 #[doc(alias = "constant_time_eq")]
3513 #[must_use]
3514 pub fn constant_time_eq_public_len(&self, other: &[u8]) -> bool {
3515 constant_time_eq_public_len(self.as_bytes(), other)
3516 }
3517
3518 #[must_use]
3526 pub fn into_exposed_array(mut self) -> ExposedDecodedArray<CAP> {
3527 let len = self.len;
3528 self.len = 0;
3529 ExposedDecodedArray::from_array(core::mem::replace(&mut self.bytes, [0u8; CAP]), len)
3530 }
3531
3532 pub fn clear(&mut self) {
3534 wipe_bytes(&mut self.bytes);
3535 self.len = 0;
3536 }
3537
3538 pub fn clear_tail(&mut self) {
3540 wipe_tail(&mut self.bytes, self.len);
3541 }
3542}
3543
3544impl<const CAP: usize> AsRef<[u8]> for DecodedBuffer<CAP> {
3545 fn as_ref(&self) -> &[u8] {
3546 self.as_bytes()
3547 }
3548}
3549
3550impl<const CAP: usize> Clone for DecodedBuffer<CAP> {
3551 fn clone(&self) -> Self {
3561 let mut output = Self::new();
3562 output.bytes[..self.len].copy_from_slice(self.as_bytes());
3563 output.len = self.len;
3564 output
3565 }
3566}
3567
3568impl<const CAP: usize> core::fmt::Debug for DecodedBuffer<CAP> {
3569 fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
3570 formatter
3571 .debug_struct("DecodedBuffer")
3572 .field("bytes", &"<redacted>")
3573 .field("len", &self.len)
3574 .field("capacity", &CAP)
3575 .finish()
3576 }
3577}
3578
3579impl<const CAP: usize> Default for DecodedBuffer<CAP> {
3580 fn default() -> Self {
3581 Self::new()
3582 }
3583}
3584
3585impl<const CAP: usize> Drop for DecodedBuffer<CAP> {
3586 fn drop(&mut self) {
3587 self.clear();
3588 }
3589}
3590
3591impl<const CAP: usize> TryFrom<&[u8]> for DecodedBuffer<CAP> {
3592 type Error = DecodeError;
3593
3594 fn try_from(input: &[u8]) -> Result<Self, Self::Error> {
3599 STANDARD.decode_buffer(input)
3600 }
3601}
3602
3603impl<const CAP: usize, const N: usize> TryFrom<&[u8; N]> for DecodedBuffer<CAP> {
3604 type Error = DecodeError;
3605
3606 fn try_from(input: &[u8; N]) -> Result<Self, Self::Error> {
3612 Self::try_from(&input[..])
3613 }
3614}
3615
3616impl<const CAP: usize> TryFrom<&str> for DecodedBuffer<CAP> {
3617 type Error = DecodeError;
3618
3619 fn try_from(input: &str) -> Result<Self, Self::Error> {
3624 Self::try_from(input.as_bytes())
3625 }
3626}
3627
3628impl<const CAP: usize> core::str::FromStr for DecodedBuffer<CAP> {
3629 type Err = DecodeError;
3630
3631 fn from_str(input: &str) -> Result<Self, Self::Err> {
3636 Self::try_from(input)
3637 }
3638}
3639
3640#[cfg(feature = "alloc")]
3668pub struct SecretBuffer {
3669 bytes: alloc::vec::Vec<u8>,
3670}
3671
3672#[cfg(feature = "alloc")]
3680pub struct ExposedSecretVec {
3681 bytes: alloc::vec::Vec<u8>,
3682}
3683
3684#[cfg(feature = "alloc")]
3685impl ExposedSecretVec {
3686 #[must_use]
3688 pub fn from_vec(mut bytes: alloc::vec::Vec<u8>) -> Self {
3689 wipe_vec_spare_capacity(&mut bytes);
3690 Self { bytes }
3691 }
3692
3693 #[must_use]
3695 pub fn len(&self) -> usize {
3696 self.bytes.len()
3697 }
3698
3699 #[must_use]
3701 pub fn is_empty(&self) -> bool {
3702 self.bytes.is_empty()
3703 }
3704
3705 #[must_use]
3710 pub fn expose_secret(&self) -> &[u8] {
3711 &self.bytes
3712 }
3713
3714 #[must_use]
3719 pub fn expose_secret_mut(&mut self) -> &mut [u8] {
3720 &mut self.bytes
3721 }
3722
3723 #[must_use = "caller must zeroize the returned Vec"]
3729 pub fn into_exposed_unprotected_vec_caller_must_zeroize(mut self) -> alloc::vec::Vec<u8> {
3730 core::mem::take(&mut self.bytes)
3731 }
3732}
3733
3734#[cfg(feature = "alloc")]
3735impl core::fmt::Debug for ExposedSecretVec {
3736 fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
3737 formatter
3738 .debug_struct("ExposedSecretVec")
3739 .field("bytes", &"<redacted>")
3740 .field("len", &self.len())
3741 .finish()
3742 }
3743}
3744
3745#[cfg(feature = "alloc")]
3746impl core::fmt::Display for ExposedSecretVec {
3747 fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
3748 formatter.write_str("<redacted>")
3749 }
3750}
3751
3752#[cfg(feature = "alloc")]
3753impl Drop for ExposedSecretVec {
3754 fn drop(&mut self) {
3755 wipe_vec_all(&mut self.bytes);
3756 }
3757}
3758
3759#[cfg(feature = "alloc")]
3760impl AsRef<[u8]> for ExposedSecretVec {
3761 fn as_ref(&self) -> &[u8] {
3762 self.expose_secret()
3763 }
3764}
3765
3766#[cfg(feature = "alloc")]
3767impl AsMut<[u8]> for ExposedSecretVec {
3768 fn as_mut(&mut self) -> &mut [u8] {
3769 self.expose_secret_mut()
3770 }
3771}
3772
3773#[cfg(feature = "alloc")]
3781pub struct ExposedSecretString {
3782 text: alloc::string::String,
3783}
3784
3785#[cfg(feature = "alloc")]
3786impl ExposedSecretString {
3787 #[must_use]
3789 pub fn from_string(text: alloc::string::String) -> Self {
3790 let mut bytes = text.into_bytes();
3791 wipe_vec_spare_capacity(&mut bytes);
3792 let text = string_from_validated_secret_bytes(bytes);
3793 Self { text }
3794 }
3795
3796 #[must_use]
3798 pub fn len(&self) -> usize {
3799 self.text.len()
3800 }
3801
3802 #[must_use]
3804 pub fn is_empty(&self) -> bool {
3805 self.text.is_empty()
3806 }
3807
3808 #[must_use]
3813 pub fn expose_secret(&self) -> &str {
3814 &self.text
3815 }
3816
3817 #[must_use]
3822 pub fn expose_secret_bytes(&self) -> &[u8] {
3823 self.text.as_bytes()
3824 }
3825
3826 #[must_use = "caller must zeroize the returned String"]
3832 pub fn into_exposed_unprotected_string_caller_must_zeroize(mut self) -> alloc::string::String {
3833 core::mem::take(&mut self.text)
3834 }
3835}
3836
3837#[cfg(feature = "alloc")]
3838impl core::fmt::Debug for ExposedSecretString {
3839 fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
3840 formatter
3841 .debug_struct("ExposedSecretString")
3842 .field("text", &"<redacted>")
3843 .field("len", &self.len())
3844 .finish()
3845 }
3846}
3847
3848#[cfg(feature = "alloc")]
3849impl core::fmt::Display for ExposedSecretString {
3850 fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
3851 formatter.write_str("<redacted>")
3852 }
3853}
3854
3855#[cfg(feature = "alloc")]
3856impl Drop for ExposedSecretString {
3857 fn drop(&mut self) {
3858 let mut bytes = core::mem::take(&mut self.text).into_bytes();
3859 wipe_vec_all(&mut bytes);
3860 }
3861}
3862
3863#[cfg(feature = "alloc")]
3864impl AsRef<str> for ExposedSecretString {
3865 fn as_ref(&self) -> &str {
3866 self.expose_secret()
3867 }
3868}
3869
3870#[cfg(feature = "alloc")]
3871impl SecretBuffer {
3872 #[must_use]
3874 pub fn from_vec(mut bytes: alloc::vec::Vec<u8>) -> Self {
3875 wipe_vec_spare_capacity(&mut bytes);
3876 Self { bytes }
3877 }
3878
3879 #[must_use]
3881 pub fn from_slice(bytes: &[u8]) -> Self {
3882 Self::from_vec(bytes.to_vec())
3883 }
3884
3885 #[must_use]
3887 pub fn len(&self) -> usize {
3888 self.bytes.len()
3889 }
3890
3891 #[must_use]
3893 pub fn is_empty(&self) -> bool {
3894 self.bytes.is_empty()
3895 }
3896
3897 #[must_use]
3902 pub fn expose_secret(&self) -> &[u8] {
3903 &self.bytes
3904 }
3905
3906 pub fn expose_secret_utf8(&self) -> Result<&str, core::str::Utf8Error> {
3912 core::str::from_utf8(self.expose_secret())
3913 }
3914
3915 #[must_use]
3920 pub fn expose_secret_mut(&mut self) -> &mut [u8] {
3921 &mut self.bytes
3922 }
3923
3924 #[must_use]
3930 pub fn into_exposed_vec(mut self) -> ExposedSecretVec {
3931 ExposedSecretVec::from_vec(core::mem::take(&mut self.bytes))
3932 }
3933
3934 #[must_use = "handle invalid UTF-8 errors and keep the returned wrapper protected"]
3943 pub fn try_into_exposed_string(self) -> Result<ExposedSecretString, Self> {
3944 if core::str::from_utf8(self.expose_secret()).is_err() {
3945 return Err(self);
3946 }
3947
3948 let mut exposed = self.into_exposed_vec();
3953 let bytes = core::mem::take(&mut exposed.bytes);
3954 drop(exposed);
3955 Ok(ExposedSecretString::from_string(
3956 string_from_validated_secret_bytes(bytes),
3957 ))
3958 }
3959
3960 #[doc(alias = "constant_time_eq")]
3979 #[must_use]
3980 pub fn constant_time_eq_public_len(&self, other: &[u8]) -> bool {
3981 constant_time_eq_public_len(self.expose_secret(), other)
3982 }
3983
3984 pub fn clear(&mut self) {
3986 wipe_vec_all(&mut self.bytes);
3987 self.bytes.clear();
3988 }
3989}
3990
3991#[cfg(feature = "alloc")]
3992impl core::fmt::Debug for SecretBuffer {
3993 fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
3994 formatter
3995 .debug_struct("SecretBuffer")
3996 .field("bytes", &"<redacted>")
3997 .field("len", &self.len())
3998 .finish()
3999 }
4000}
4001
4002#[cfg(feature = "alloc")]
4003impl core::fmt::Display for SecretBuffer {
4004 fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
4005 formatter.write_str("<redacted>")
4006 }
4007}
4008
4009#[cfg(feature = "alloc")]
4010impl Drop for SecretBuffer {
4011 fn drop(&mut self) {
4012 wipe_vec_all(&mut self.bytes);
4013 }
4014}
4015
4016#[cfg(feature = "alloc")]
4017impl From<alloc::vec::Vec<u8>> for SecretBuffer {
4018 fn from(bytes: alloc::vec::Vec<u8>) -> Self {
4023 Self::from_vec(bytes)
4024 }
4025}
4026
4027#[cfg(feature = "alloc")]
4028impl From<alloc::string::String> for SecretBuffer {
4029 fn from(text: alloc::string::String) -> Self {
4034 Self::from_vec(text.into_bytes())
4035 }
4036}
4037
4038#[cfg(feature = "alloc")]
4039impl<const CAP: usize> From<EncodedBuffer<CAP>> for SecretBuffer {
4040 fn from(buffer: EncodedBuffer<CAP>) -> Self {
4046 Self::from_slice(buffer.as_bytes())
4047 }
4048}
4049
4050#[cfg(feature = "alloc")]
4051impl<const CAP: usize> From<DecodedBuffer<CAP>> for SecretBuffer {
4052 fn from(buffer: DecodedBuffer<CAP>) -> Self {
4058 Self::from_slice(buffer.as_bytes())
4059 }
4060}
4061
4062#[cfg(feature = "alloc")]
4063impl TryFrom<&[u8]> for SecretBuffer {
4064 type Error = DecodeError;
4065
4066 fn try_from(input: &[u8]) -> Result<Self, Self::Error> {
4071 STANDARD.decode_secret(input)
4072 }
4073}
4074
4075#[cfg(feature = "alloc")]
4076impl<const N: usize> TryFrom<&[u8; N]> for SecretBuffer {
4077 type Error = DecodeError;
4078
4079 fn try_from(input: &[u8; N]) -> Result<Self, Self::Error> {
4085 Self::try_from(&input[..])
4086 }
4087}
4088
4089#[cfg(feature = "alloc")]
4090impl TryFrom<&str> for SecretBuffer {
4091 type Error = DecodeError;
4092
4093 fn try_from(input: &str) -> Result<Self, Self::Error> {
4098 Self::try_from(input.as_bytes())
4099 }
4100}
4101
4102#[cfg(feature = "alloc")]
4103impl core::str::FromStr for SecretBuffer {
4104 type Err = DecodeError;
4105
4106 fn from_str(input: &str) -> Result<Self, Self::Err> {
4111 Self::try_from(input)
4112 }
4113}
4114
4115#[derive(Clone, Copy, Debug, Eq, PartialEq)]
4121pub struct Profile<A, const PAD: bool> {
4122 engine: Engine<A, PAD>,
4123 wrap: Option<LineWrap>,
4124}
4125
4126impl<A, const PAD: bool> Profile<A, PAD>
4127where
4128 A: Alphabet,
4129{
4130 #[must_use]
4132 pub const fn new(engine: Engine<A, PAD>, wrap: Option<LineWrap>) -> Self {
4133 Self { engine, wrap }
4134 }
4135
4136 #[must_use]
4142 pub const fn checked_new(engine: Engine<A, PAD>, wrap: Option<LineWrap>) -> Option<Self> {
4143 match wrap {
4144 Some(wrap) if !wrap.is_valid() => None,
4145 _ => Some(Self::new(engine, wrap)),
4146 }
4147 }
4148
4149 #[must_use]
4151 pub const fn is_valid(&self) -> bool {
4152 match self.wrap {
4153 Some(wrap) => wrap.is_valid(),
4154 None => true,
4155 }
4156 }
4157
4158 #[must_use]
4160 pub const fn engine(&self) -> Engine<A, PAD> {
4161 self.engine
4162 }
4163
4164 #[must_use]
4166 pub const fn is_padded(&self) -> bool {
4167 PAD
4168 }
4169
4170 #[must_use]
4172 pub const fn is_wrapped(&self) -> bool {
4173 self.wrap.is_some()
4174 }
4175
4176 #[must_use]
4178 pub const fn line_wrap(&self) -> Option<LineWrap> {
4179 self.wrap
4180 }
4181
4182 #[must_use]
4184 pub const fn line_len(&self) -> Option<usize> {
4185 match self.wrap {
4186 Some(wrap) => Some(wrap.line_len()),
4187 None => None,
4188 }
4189 }
4190
4191 #[must_use]
4193 pub const fn line_ending(&self) -> Option<LineEnding> {
4194 match self.wrap {
4195 Some(wrap) => Some(wrap.line_ending()),
4196 None => None,
4197 }
4198 }
4199
4200 pub const fn encoded_len(&self, input_len: usize) -> Result<usize, EncodeError> {
4202 match self.wrap {
4203 Some(wrap) => wrapped_encoded_len(input_len, PAD, wrap),
4204 None => encoded_len(input_len, PAD),
4205 }
4206 }
4207
4208 #[must_use]
4211 pub const fn checked_encoded_len(&self, input_len: usize) -> Option<usize> {
4212 match self.wrap {
4213 Some(wrap) => checked_wrapped_encoded_len(input_len, PAD, wrap),
4214 None => checked_encoded_len(input_len, PAD),
4215 }
4216 }
4217
4218 pub fn decoded_len(&self, input: &[u8]) -> Result<usize, DecodeError> {
4220 match self.wrap {
4221 Some(wrap) => self.engine.decoded_len_wrapped(input, wrap),
4222 None => self.engine.decoded_len(input),
4223 }
4224 }
4225
4226 pub fn validate_result(&self, input: &[u8]) -> Result<(), DecodeError> {
4228 match self.wrap {
4229 Some(wrap) => self.engine.validate_wrapped_result(input, wrap),
4230 None => self.engine.validate_result(input),
4231 }
4232 }
4233
4234 #[must_use]
4236 pub fn validate(&self, input: &[u8]) -> bool {
4237 self.validate_result(input).is_ok()
4238 }
4239
4240 pub fn encode_slice(&self, input: &[u8], output: &mut [u8]) -> Result<usize, EncodeError> {
4242 match self.wrap {
4243 Some(wrap) => self.engine.encode_slice_wrapped(input, output, wrap),
4244 None => self.engine.encode_slice(input, output),
4245 }
4246 }
4247
4248 pub fn encode_slice_clear_tail(
4251 &self,
4252 input: &[u8],
4253 output: &mut [u8],
4254 ) -> Result<usize, EncodeError> {
4255 match self.wrap {
4256 Some(wrap) => self
4257 .engine
4258 .encode_slice_wrapped_clear_tail(input, output, wrap),
4259 None => self.engine.encode_slice_clear_tail(input, output),
4260 }
4261 }
4262
4263 pub fn encode_buffer<const CAP: usize>(
4269 &self,
4270 input: &[u8],
4271 ) -> Result<EncodedBuffer<CAP>, EncodeError> {
4272 let mut output = EncodedBuffer::new();
4273 let written = match self.encode_slice_clear_tail(input, &mut output.bytes) {
4274 Ok(written) => written,
4275 Err(err) => {
4276 output.clear();
4277 return Err(err);
4278 }
4279 };
4280 output.len = written;
4281 Ok(output)
4282 }
4283
4284 #[must_use = "handle decode errors; use crate::ct for secret-bearing payloads"]
4297 pub fn decode_slice(&self, input: &[u8], output: &mut [u8]) -> Result<usize, DecodeError> {
4298 match self.wrap {
4299 Some(wrap) => self.engine.decode_slice_wrapped(input, output, wrap),
4300 None => self.engine.decode_slice(input, output),
4301 }
4302 }
4303
4304 pub fn decode_slice_clear_tail(
4307 &self,
4308 input: &[u8],
4309 output: &mut [u8],
4310 ) -> Result<usize, DecodeError> {
4311 match self.wrap {
4312 Some(wrap) => self
4313 .engine
4314 .decode_slice_wrapped_clear_tail(input, output, wrap),
4315 None => self.engine.decode_slice_clear_tail(input, output),
4316 }
4317 }
4318
4319 pub fn decode_buffer<const CAP: usize>(
4325 &self,
4326 input: &[u8],
4327 ) -> Result<DecodedBuffer<CAP>, DecodeError> {
4328 let mut output = DecodedBuffer::new();
4329 let written = match self.decode_slice_clear_tail(input, &mut output.bytes) {
4330 Ok(written) => written,
4331 Err(err) => {
4332 output.clear();
4333 return Err(err);
4334 }
4335 };
4336 output.len = written;
4337 Ok(output)
4338 }
4339
4340 pub fn decode_in_place<'a>(&self, buffer: &'a mut [u8]) -> Result<&'a mut [u8], DecodeError> {
4369 match self.wrap {
4370 Some(wrap) => self.engine.decode_in_place_wrapped(buffer, wrap),
4371 None => self.engine.decode_in_place(buffer),
4372 }
4373 }
4374
4375 pub fn decode_in_place_clear_tail<'a>(
4394 &self,
4395 buffer: &'a mut [u8],
4396 ) -> Result<&'a mut [u8], DecodeError> {
4397 match self.wrap {
4398 Some(wrap) => self.engine.decode_in_place_wrapped_clear_tail(buffer, wrap),
4399 None => self.engine.decode_in_place_clear_tail(buffer),
4400 }
4401 }
4402
4403 #[cfg(feature = "alloc")]
4405 #[must_use = "for secret-bearing payloads use encode_secret, which returns a redacted buffer with drop-time cleanup"]
4406 pub fn encode_vec(&self, input: &[u8]) -> Result<alloc::vec::Vec<u8>, EncodeError> {
4407 match self.wrap {
4408 Some(wrap) => self.engine.encode_wrapped_vec(input, wrap),
4409 None => self.engine.encode_vec(input),
4410 }
4411 }
4412
4413 #[cfg(feature = "alloc")]
4415 pub fn encode_secret(&self, input: &[u8]) -> Result<SecretBuffer, EncodeError> {
4416 self.encode_vec(input).map(SecretBuffer::from_vec)
4417 }
4418
4419 #[cfg(feature = "alloc")]
4421 pub fn encode_string(&self, input: &[u8]) -> Result<alloc::string::String, EncodeError> {
4422 match self.wrap {
4423 Some(wrap) => self.engine.encode_wrapped_string(input, wrap),
4424 None => self.engine.encode_string(input),
4425 }
4426 }
4427
4428 #[cfg(feature = "alloc")]
4430 #[must_use = "for secret-bearing payloads use decode_secret, which returns a redacted buffer with drop-time cleanup"]
4431 pub fn decode_vec(&self, input: &[u8]) -> Result<alloc::vec::Vec<u8>, DecodeError> {
4432 match self.wrap {
4433 Some(wrap) => self.engine.decode_wrapped_vec(input, wrap),
4434 None => self.engine.decode_vec(input),
4435 }
4436 }
4437
4438 #[cfg(feature = "alloc")]
4440 pub fn decode_secret(&self, input: &[u8]) -> Result<SecretBuffer, DecodeError> {
4441 self.decode_vec(input).map(SecretBuffer::from_vec)
4442 }
4443}
4444
4445impl<A, const PAD: bool> Default for Profile<A, PAD>
4446where
4447 A: Alphabet,
4448{
4449 fn default() -> Self {
4450 Self::new(Engine::new(), None)
4451 }
4452}
4453
4454impl<A, const PAD: bool> core::fmt::Display for Profile<A, PAD>
4455where
4456 A: Alphabet,
4457{
4458 fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
4459 match self.wrap {
4460 Some(wrap) => write!(formatter, "padded={PAD} wrap={wrap}"),
4461 None => write!(formatter, "padded={PAD} wrap=none"),
4462 }
4463 }
4464}
4465
4466impl<A, const PAD: bool> From<Engine<A, PAD>> for Profile<A, PAD>
4467where
4468 A: Alphabet,
4469{
4470 fn from(engine: Engine<A, PAD>) -> Self {
4471 Self::new(engine, None)
4472 }
4473}
4474
4475#[doc(alias = "ct")]
4481#[doc(alias = "constant_time")]
4482#[doc(alias = "sensitive")]
4483pub const MIME: Profile<Standard, true> = Profile::new(STANDARD, Some(LineWrap::MIME));
4484
4485#[doc(alias = "ct")]
4491#[doc(alias = "constant_time")]
4492#[doc(alias = "sensitive")]
4493pub const PEM: Profile<Standard, true> = Profile::new(STANDARD, Some(LineWrap::PEM));
4494
4495#[doc(alias = "ct")]
4501#[doc(alias = "constant_time")]
4502#[doc(alias = "sensitive")]
4503pub const PEM_CRLF: Profile<Standard, true> = Profile::new(STANDARD, Some(LineWrap::PEM_CRLF));
4504
4505#[doc(alias = "ct")]
4513#[doc(alias = "constant_time")]
4514#[doc(alias = "sensitive")]
4515pub const BCRYPT: Profile<Bcrypt, false> = Profile::new(BCRYPT_NO_PAD, None);
4516
4517#[doc(alias = "ct")]
4525#[doc(alias = "constant_time")]
4526#[doc(alias = "sensitive")]
4527pub const CRYPT: Profile<Crypt, false> = Profile::new(CRYPT_NO_PAD, None);
4528
4529pub const fn encoded_len(input_len: usize, padded: bool) -> Result<usize, EncodeError> {
4544 match checked_encoded_len(input_len, padded) {
4545 Some(len) => Ok(len),
4546 None => Err(EncodeError::LengthOverflow),
4547 }
4548}
4549
4550pub const fn wrapped_encoded_len(
4564 input_len: usize,
4565 padded: bool,
4566 wrap: LineWrap,
4567) -> Result<usize, EncodeError> {
4568 if wrap.line_len == 0 {
4569 return Err(EncodeError::InvalidLineWrap { line_len: 0 });
4570 }
4571
4572 let Some(encoded) = checked_encoded_len(input_len, padded) else {
4573 return Err(EncodeError::LengthOverflow);
4574 };
4575 if encoded == 0 {
4576 return Ok(0);
4577 }
4578
4579 let breaks = (encoded - 1) / wrap.line_len;
4580 let Some(line_ending_bytes) = breaks.checked_mul(wrap.line_ending.byte_len()) else {
4581 return Err(EncodeError::LengthOverflow);
4582 };
4583 match encoded.checked_add(line_ending_bytes) {
4584 Some(len) => Ok(len),
4585 None => Err(EncodeError::LengthOverflow),
4586 }
4587}
4588
4589#[must_use]
4605pub const fn checked_wrapped_encoded_len(
4606 input_len: usize,
4607 padded: bool,
4608 wrap: LineWrap,
4609) -> Option<usize> {
4610 if wrap.line_len == 0 {
4611 return None;
4612 }
4613
4614 let Some(encoded) = checked_encoded_len(input_len, padded) else {
4615 return None;
4616 };
4617 if encoded == 0 {
4618 return Some(0);
4619 }
4620
4621 let breaks = (encoded - 1) / wrap.line_len;
4622 let Some(line_ending_bytes) = breaks.checked_mul(wrap.line_ending.byte_len()) else {
4623 return None;
4624 };
4625 encoded.checked_add(line_ending_bytes)
4626}
4627
4628#[must_use]
4639pub const fn checked_encoded_len(input_len: usize, padded: bool) -> Option<usize> {
4640 let groups = input_len / 3;
4641 if groups > usize::MAX / 4 {
4642 return None;
4643 }
4644 let full = groups * 4;
4645 let rem = input_len % 3;
4646 if rem == 0 {
4647 Some(full)
4648 } else if padded {
4649 full.checked_add(4)
4650 } else {
4651 full.checked_add(rem + 1)
4652 }
4653}
4654
4655#[must_use]
4673pub fn constant_time_eq_fixed_width<const N: usize>(left: &[u8; N], right: &[u8; N]) -> bool {
4674 constant_time_eq_fixed_width_array(left, right)
4675}
4676
4677#[must_use]
4688pub const fn decoded_capacity(encoded_len: usize) -> usize {
4689 let rem = encoded_len % 4;
4690 encoded_len / 4 * 3
4691 + if rem == 2 {
4692 1
4693 } else if rem == 3 {
4694 2
4695 } else {
4696 0
4697 }
4698}
4699
4700pub fn decoded_len(input: &[u8], padded: bool) -> Result<usize, DecodeError> {
4714 if padded {
4715 decoded_len_padded(input)
4716 } else {
4717 decoded_len_unpadded(input)
4718 }
4719}
4720
4721#[macro_export]
4758macro_rules! define_alphabet {
4759 ($(#[$meta:meta])* $vis:vis struct $name:ident = $encode:expr;) => {
4760 $(#[$meta])*
4761 #[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
4762 $vis struct $name;
4763
4764 impl $crate::Alphabet for $name {
4765 const ENCODE: [u8; 64] = *$encode;
4766
4767 #[inline]
4768 fn decode(byte: u8) -> Option<u8> {
4769 $crate::decode_alphabet_byte(byte, &Self::ENCODE)
4770 }
4771 }
4772
4773 const _: [(); 1] = [(); match $crate::validate_alphabet(
4774 &<$name as $crate::Alphabet>::ENCODE,
4775 ) {
4776 Ok(()) => 1,
4777 Err(_) => 0,
4778 }];
4779 };
4780}
4781
4782pub const fn validate_alphabet(encode: &[u8; 64]) -> Result<(), AlphabetError> {
4795 let mut index = 0;
4796 while index < encode.len() {
4797 let byte = encode[index];
4798 if !is_visible_ascii(byte) {
4799 return Err(AlphabetError::InvalidByte { index, byte });
4800 }
4801 if byte == b'=' {
4802 return Err(AlphabetError::PaddingByte { index });
4803 }
4804
4805 let mut duplicate = index + 1;
4806 while duplicate < encode.len() {
4807 if encode[duplicate] == byte {
4808 return Err(AlphabetError::DuplicateByte {
4809 first: index,
4810 second: duplicate,
4811 byte,
4812 });
4813 }
4814 duplicate += 1;
4815 }
4816
4817 index += 1;
4818 }
4819
4820 Ok(())
4821}
4822
4823#[must_use]
4851pub const fn decode_alphabet_byte(byte: u8, encode: &[u8; 64]) -> Option<u8> {
4852 let mut index = 0;
4853 let mut candidate = 0;
4854 let mut decoded = 0;
4855 let mut valid = 0;
4856 while index < encode.len() {
4857 let matches = ct_mask_eq_u8(byte, encode[index]);
4858 decoded |= candidate & matches;
4859 valid |= matches;
4860 index += 1;
4861 candidate += 1;
4862 }
4863
4864 if valid == 0 { None } else { Some(decoded) }
4865}
4866
4867pub trait Alphabet {
4884 const ENCODE: [u8; 64];
4886
4887 #[must_use]
4898 fn encode(value: u8) -> u8 {
4899 encode_alphabet_value(value, &Self::ENCODE)
4900 }
4901
4902 fn decode(byte: u8) -> Option<u8>;
4909}
4910
4911const fn is_visible_ascii(byte: u8) -> bool {
4912 byte >= 0x21 && byte <= 0x7e
4913}
4914
4915#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
4917pub struct Standard;
4918
4919impl Alphabet for Standard {
4920 const ENCODE: [u8; 64] = *b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
4921
4922 #[inline]
4923 fn encode(value: u8) -> u8 {
4924 encode_ascii_base64(value, Self::ENCODE[62], Self::ENCODE[63])
4925 }
4926
4927 #[inline]
4928 fn decode(byte: u8) -> Option<u8> {
4929 decode_ascii_base64(byte, Self::ENCODE[62], Self::ENCODE[63])
4930 }
4931}
4932
4933#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
4935pub struct UrlSafe;
4936
4937impl Alphabet for UrlSafe {
4938 const ENCODE: [u8; 64] = *b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
4939
4940 #[inline]
4941 fn encode(value: u8) -> u8 {
4942 encode_ascii_base64(value, Self::ENCODE[62], Self::ENCODE[63])
4943 }
4944
4945 #[inline]
4946 fn decode(byte: u8) -> Option<u8> {
4947 decode_ascii_base64(byte, Self::ENCODE[62], Self::ENCODE[63])
4948 }
4949}
4950
4951#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
4957pub struct Bcrypt;
4958
4959impl Alphabet for Bcrypt {
4960 const ENCODE: [u8; 64] = *b"./ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
4961
4962 #[inline]
4963 fn decode(byte: u8) -> Option<u8> {
4964 decode_alphabet_byte(byte, &Self::ENCODE)
4965 }
4966}
4967
4968#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
4973pub struct Crypt;
4974
4975impl Alphabet for Crypt {
4976 const ENCODE: [u8; 64] = *b"./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
4977
4978 #[inline]
4979 fn decode(byte: u8) -> Option<u8> {
4980 decode_alphabet_byte(byte, &Self::ENCODE)
4981 }
4982}
4983
4984#[inline]
4985const fn encode_base64_value<A: Alphabet>(value: u8) -> u8 {
4986 encode_alphabet_value(value, &A::ENCODE)
4987}
4988
4989#[inline]
4990fn encode_base64_value_runtime<A: Alphabet>(value: u8) -> u8 {
4991 A::encode(value)
4992}
4993
4994#[inline]
4995const fn encode_alphabet_value(value: u8, encode: &[u8; 64]) -> u8 {
4996 let mut output = 0;
4997 let mut index = 0;
4998 let mut candidate = 0;
4999 while index < encode.len() {
5000 output |= encode[index] & ct_mask_eq_u8(value, candidate);
5001 index += 1;
5002 candidate += 1;
5003 }
5004 output
5005}
5006
5007#[inline]
5008const fn encode_ascii_base64(value: u8, value_62_byte: u8, value_63_byte: u8) -> u8 {
5009 let upper = ct_mask_lt_u8(value, 26);
5010 let lower = ct_mask_lt_u8(value.wrapping_sub(26), 26);
5011 let digit = ct_mask_lt_u8(value.wrapping_sub(52), 10);
5012 let value_62 = ct_mask_eq_u8(value, 0x3e);
5013 let value_63 = ct_mask_eq_u8(value, 0x3f);
5014
5015 (value.wrapping_add(b'A') & upper)
5016 | (value.wrapping_sub(26).wrapping_add(b'a') & lower)
5017 | (value.wrapping_sub(52).wrapping_add(b'0') & digit)
5018 | (value_62_byte & value_62)
5019 | (value_63_byte & value_63)
5020}
5021
5022#[inline]
5023fn decode_ascii_base64(byte: u8, value_62_byte: u8, value_63_byte: u8) -> Option<u8> {
5024 let upper = ct_mask_lt_u8(byte.wrapping_sub(b'A'), 26);
5025 let lower = ct_mask_lt_u8(byte.wrapping_sub(b'a'), 26);
5026 let digit = ct_mask_lt_u8(byte.wrapping_sub(b'0'), 10);
5027 let value_62 = ct_mask_eq_u8(byte, value_62_byte);
5028 let value_63 = ct_mask_eq_u8(byte, value_63_byte);
5029 let valid = upper | lower | digit | value_62 | value_63;
5030
5031 let decoded = (byte.wrapping_sub(b'A') & upper)
5032 | (byte.wrapping_sub(b'a').wrapping_add(26) & lower)
5033 | (byte.wrapping_sub(b'0').wrapping_add(52) & digit)
5034 | (0x3e & value_62)
5035 | (0x3f & value_63);
5036
5037 if valid == 0 { None } else { Some(decoded) }
5038}
5039
5040#[inline]
5041const fn ct_mask_bit(bit: u8) -> u8 {
5042 0u8.wrapping_sub(bit & 1)
5043}
5044
5045#[inline]
5046const fn ct_mask_nonzero_u8(value: u8) -> u8 {
5047 let wide = value as u16;
5048 let negative = 0u16.wrapping_sub(wide);
5049 let nonzero = ((wide | negative) >> 8) as u8;
5050 ct_mask_bit(nonzero)
5051}
5052
5053#[inline]
5054const fn ct_mask_eq_u8(left: u8, right: u8) -> u8 {
5055 !ct_mask_nonzero_u8(left ^ right)
5056}
5057
5058#[inline]
5059const fn ct_mask_lt_u8(left: u8, right: u8) -> u8 {
5060 let diff = (left as u16).wrapping_sub(right as u16);
5061 ct_mask_bit((diff >> 8) as u8)
5062}
5063
5064#[inline(never)]
5065fn constant_time_eq_public_len(left: &[u8], right: &[u8]) -> bool {
5066 if left.len() != right.len() {
5067 return false;
5068 }
5069
5070 constant_time_eq_same_len(left, right)
5071}
5072
5073#[inline(never)]
5074fn constant_time_eq_fixed_width_array<const N: usize>(left: &[u8; N], right: &[u8; N]) -> bool {
5075 constant_time_eq_same_len(left, right)
5076}
5077
5078#[inline(never)]
5079#[allow(unsafe_code)]
5080fn constant_time_eq_same_len(left: &[u8], right: &[u8]) -> bool {
5081 let mut diff = 0u8;
5082 for (left, right) in left.iter().zip(right) {
5083 diff = core::hint::black_box(
5084 core::hint::black_box(diff) | core::hint::black_box(*left ^ *right),
5085 );
5086 diff = unsafe { core::ptr::read_volatile(&raw const diff) };
5090 }
5091 ct_error_gate_barrier(diff, 0);
5092 let result = unsafe { core::ptr::read_volatile(&raw const diff) };
5096 result == 0
5097}
5098
5099#[cfg(feature = "alloc")]
5100#[allow(unsafe_code)]
5101fn string_from_validated_secret_bytes(bytes: alloc::vec::Vec<u8>) -> alloc::string::String {
5102 unsafe { alloc::string::String::from_utf8_unchecked(bytes) }
5107}
5108
5109mod backend {
5110 use super::{
5111 Alphabet, DecodeError, EncodeError, checked_encoded_len, decode_padded, decode_unpadded,
5112 encode_base64_value_runtime,
5113 };
5114
5115 pub(super) fn encode_slice<A, const PAD: bool>(
5116 input: &[u8],
5117 output: &mut [u8],
5118 ) -> Result<usize, EncodeError>
5119 where
5120 A: Alphabet,
5121 {
5122 #[cfg(feature = "simd")]
5123 match super::simd::active_backend() {
5124 super::simd::ActiveBackend::Scalar => {}
5125 }
5126
5127 scalar_encode_slice::<A, PAD>(input, output)
5128 }
5129
5130 pub(super) fn decode_slice<A, const PAD: bool>(
5131 input: &[u8],
5132 output: &mut [u8],
5133 ) -> Result<usize, DecodeError>
5134 where
5135 A: Alphabet,
5136 {
5137 #[cfg(feature = "simd")]
5138 match super::simd::active_backend() {
5139 super::simd::ActiveBackend::Scalar => {}
5140 }
5141
5142 scalar_decode_slice::<A, PAD>(input, output)
5143 }
5144
5145 #[cfg(test)]
5146 pub(super) fn scalar_reference_encode_slice<A, const PAD: bool>(
5147 input: &[u8],
5148 output: &mut [u8],
5149 ) -> Result<usize, EncodeError>
5150 where
5151 A: Alphabet,
5152 {
5153 scalar_encode_slice::<A, PAD>(input, output)
5154 }
5155
5156 #[cfg(test)]
5157 pub(super) fn scalar_reference_decode_slice<A, const PAD: bool>(
5158 input: &[u8],
5159 output: &mut [u8],
5160 ) -> Result<usize, DecodeError>
5161 where
5162 A: Alphabet,
5163 {
5164 scalar_decode_slice::<A, PAD>(input, output)
5165 }
5166
5167 fn scalar_encode_slice<A, const PAD: bool>(
5168 input: &[u8],
5169 output: &mut [u8],
5170 ) -> Result<usize, EncodeError>
5171 where
5172 A: Alphabet,
5173 {
5174 let required = checked_encoded_len(input.len(), PAD).ok_or(EncodeError::LengthOverflow)?;
5175 if output.len() < required {
5176 return Err(EncodeError::OutputTooSmall {
5177 required,
5178 available: output.len(),
5179 });
5180 }
5181
5182 let mut read = 0;
5183 let mut write = 0;
5184 while read + 3 <= input.len() {
5185 let b0 = input[read];
5186 let b1 = input[read + 1];
5187 let b2 = input[read + 2];
5188
5189 output[write] = encode_base64_value_runtime::<A>(b0 >> 2);
5190 output[write + 1] =
5191 encode_base64_value_runtime::<A>(((b0 & 0b0000_0011) << 4) | (b1 >> 4));
5192 output[write + 2] =
5193 encode_base64_value_runtime::<A>(((b1 & 0b0000_1111) << 2) | (b2 >> 6));
5194 output[write + 3] = encode_base64_value_runtime::<A>(b2 & 0b0011_1111);
5195
5196 read += 3;
5197 write += 4;
5198 }
5199
5200 match input.len() - read {
5201 0 => {}
5202 1 => {
5203 let b0 = input[read];
5204 output[write] = encode_base64_value_runtime::<A>(b0 >> 2);
5205 output[write + 1] = encode_base64_value_runtime::<A>((b0 & 0b0000_0011) << 4);
5206 write += 2;
5207 if PAD {
5208 output[write] = b'=';
5209 output[write + 1] = b'=';
5210 write += 2;
5211 }
5212 }
5213 2 => {
5214 let b0 = input[read];
5215 let b1 = input[read + 1];
5216 output[write] = encode_base64_value_runtime::<A>(b0 >> 2);
5217 output[write + 1] =
5218 encode_base64_value_runtime::<A>(((b0 & 0b0000_0011) << 4) | (b1 >> 4));
5219 output[write + 2] = encode_base64_value_runtime::<A>((b1 & 0b0000_1111) << 2);
5220 write += 3;
5221 if PAD {
5222 output[write] = b'=';
5223 write += 1;
5224 }
5225 }
5226 _ => unreachable!(),
5227 }
5228
5229 Ok(write)
5230 }
5231
5232 fn scalar_decode_slice<A, const PAD: bool>(
5233 input: &[u8],
5234 output: &mut [u8],
5235 ) -> Result<usize, DecodeError>
5236 where
5237 A: Alphabet,
5238 {
5239 if input.is_empty() {
5240 return Ok(0);
5241 }
5242
5243 if PAD {
5244 decode_padded::<A>(input, output)
5245 } else {
5246 decode_unpadded::<A>(input, output)
5247 }
5248 }
5249}
5250
5251pub struct Engine<A, const PAD: bool> {
5253 alphabet: core::marker::PhantomData<A>,
5254}
5255
5256impl<A, const PAD: bool> Clone for Engine<A, PAD> {
5257 fn clone(&self) -> Self {
5258 *self
5259 }
5260}
5261
5262impl<A, const PAD: bool> Copy for Engine<A, PAD> {}
5263
5264impl<A, const PAD: bool> core::fmt::Debug for Engine<A, PAD> {
5265 fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
5266 formatter
5267 .debug_struct("Engine")
5268 .field("padded", &PAD)
5269 .finish()
5270 }
5271}
5272
5273impl<A, const PAD: bool> core::fmt::Display for Engine<A, PAD> {
5274 fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
5275 write!(formatter, "padded={PAD}")
5276 }
5277}
5278
5279impl<A, const PAD: bool> Default for Engine<A, PAD> {
5280 fn default() -> Self {
5281 Self {
5282 alphabet: core::marker::PhantomData,
5283 }
5284 }
5285}
5286
5287impl<A, const PAD: bool> Eq for Engine<A, PAD> {}
5288
5289impl<A, const PAD: bool> PartialEq for Engine<A, PAD> {
5290 fn eq(&self, _other: &Self) -> bool {
5291 true
5292 }
5293}
5294
5295impl<A, const PAD: bool> Engine<A, PAD>
5296where
5297 A: Alphabet,
5298{
5299 #[must_use]
5301 pub const fn new() -> Self {
5302 Self {
5303 alphabet: core::marker::PhantomData,
5304 }
5305 }
5306
5307 #[must_use]
5309 pub const fn is_padded(&self) -> bool {
5310 PAD
5311 }
5312
5313 #[must_use]
5318 pub const fn profile(&self) -> Profile<A, PAD> {
5319 Profile::new(*self, None)
5320 }
5321
5322 #[must_use]
5328 pub const fn ct_decoder(&self) -> ct::CtEngine<A, PAD> {
5329 ct::CtEngine::new()
5330 }
5331
5332 #[cfg(feature = "stream")]
5346 #[must_use]
5347 pub fn encoder_writer<W>(&self, inner: W) -> stream::Encoder<W, A, PAD> {
5348 stream::Encoder::new(inner, *self)
5349 }
5350
5351 #[cfg(feature = "stream")]
5371 #[must_use]
5372 pub fn decoder_writer<W>(&self, inner: W) -> stream::Decoder<W, A, PAD> {
5373 stream::Decoder::new(inner, *self)
5374 }
5375
5376 #[cfg(feature = "stream")]
5391 #[must_use]
5392 pub fn encoder_reader<R>(&self, inner: R) -> stream::EncoderReader<R, A, PAD> {
5393 stream::EncoderReader::new(inner, *self)
5394 }
5395
5396 #[cfg(feature = "stream")]
5417 #[must_use]
5418 pub fn decoder_reader<R>(&self, inner: R) -> stream::DecoderReader<R, A, PAD> {
5419 stream::DecoderReader::new(inner, *self)
5420 }
5421
5422 pub const fn encoded_len(&self, input_len: usize) -> Result<usize, EncodeError> {
5424 encoded_len(input_len, PAD)
5425 }
5426
5427 #[must_use]
5429 pub const fn checked_encoded_len(&self, input_len: usize) -> Option<usize> {
5430 checked_encoded_len(input_len, PAD)
5431 }
5432
5433 pub const fn wrapped_encoded_len(
5438 &self,
5439 input_len: usize,
5440 wrap: LineWrap,
5441 ) -> Result<usize, EncodeError> {
5442 wrapped_encoded_len(input_len, PAD, wrap)
5443 }
5444
5445 #[must_use]
5448 pub const fn checked_wrapped_encoded_len(
5449 &self,
5450 input_len: usize,
5451 wrap: LineWrap,
5452 ) -> Option<usize> {
5453 checked_wrapped_encoded_len(input_len, PAD, wrap)
5454 }
5455
5456 pub fn decoded_len(&self, input: &[u8]) -> Result<usize, DecodeError> {
5461 decoded_len(input, PAD)
5462 }
5463
5464 pub fn decoded_len_legacy(&self, input: &[u8]) -> Result<usize, DecodeError> {
5470 validate_legacy_decode::<A, PAD>(input)
5471 }
5472
5473 pub fn decoded_len_wrapped(&self, input: &[u8], wrap: LineWrap) -> Result<usize, DecodeError> {
5480 validate_wrapped_decode::<A, PAD>(input, wrap)
5481 }
5482
5483 pub fn validate_result(&self, input: &[u8]) -> Result<(), DecodeError> {
5501 validate_decode::<A, PAD>(input).map(|_| ())
5502 }
5503
5504 #[must_use]
5520 pub fn validate(&self, input: &[u8]) -> bool {
5521 self.validate_result(input).is_ok()
5522 }
5523
5524 pub fn validate_legacy_result(&self, input: &[u8]) -> Result<(), DecodeError> {
5539 validate_legacy_decode::<A, PAD>(input).map(|_| ())
5540 }
5541
5542 #[must_use]
5556 pub fn validate_legacy(&self, input: &[u8]) -> bool {
5557 self.validate_legacy_result(input).is_ok()
5558 }
5559
5560 pub fn validate_wrapped_result(&self, input: &[u8], wrap: LineWrap) -> Result<(), DecodeError> {
5576 validate_wrapped_decode::<A, PAD>(input, wrap).map(|_| ())
5577 }
5578
5579 #[must_use]
5593 pub fn validate_wrapped(&self, input: &[u8], wrap: LineWrap) -> bool {
5594 self.validate_wrapped_result(input, wrap).is_ok()
5595 }
5596
5597 #[must_use]
5629 pub const fn encode_array<const INPUT_LEN: usize, const OUTPUT_LEN: usize>(
5630 &self,
5631 input: &[u8; INPUT_LEN],
5632 ) -> [u8; OUTPUT_LEN] {
5633 let Some(required) = checked_encoded_len(INPUT_LEN, PAD) else {
5634 panic!("encoded base64 length overflows usize");
5635 };
5636 assert!(
5637 required == OUTPUT_LEN,
5638 "base64 output array has incorrect length"
5639 );
5640
5641 let mut output = [0u8; OUTPUT_LEN];
5642 let mut read = 0;
5643 let mut write = 0;
5644 while INPUT_LEN - read >= 3 {
5645 let b0 = input[read];
5646 let b1 = input[read + 1];
5647 let b2 = input[read + 2];
5648
5649 output[write] = encode_base64_value::<A>(b0 >> 2);
5650 output[write + 1] = encode_base64_value::<A>(((b0 & 0b0000_0011) << 4) | (b1 >> 4));
5651 output[write + 2] = encode_base64_value::<A>(((b1 & 0b0000_1111) << 2) | (b2 >> 6));
5652 output[write + 3] = encode_base64_value::<A>(b2 & 0b0011_1111);
5653
5654 read += 3;
5655 write += 4;
5656 }
5657
5658 match INPUT_LEN - read {
5659 0 => {}
5660 1 => {
5661 let b0 = input[read];
5662 output[write] = encode_base64_value::<A>(b0 >> 2);
5663 output[write + 1] = encode_base64_value::<A>((b0 & 0b0000_0011) << 4);
5664 write += 2;
5665 if PAD {
5666 output[write] = b'=';
5667 output[write + 1] = b'=';
5668 }
5669 }
5670 2 => {
5671 let b0 = input[read];
5672 let b1 = input[read + 1];
5673 output[write] = encode_base64_value::<A>(b0 >> 2);
5674 output[write + 1] = encode_base64_value::<A>(((b0 & 0b0000_0011) << 4) | (b1 >> 4));
5675 output[write + 2] = encode_base64_value::<A>((b1 & 0b0000_1111) << 2);
5676 if PAD {
5677 output[write + 3] = b'=';
5678 }
5679 }
5680 _ => unreachable!(),
5681 }
5682
5683 output
5684 }
5685
5686 pub fn encode_slice(&self, input: &[u8], output: &mut [u8]) -> Result<usize, EncodeError> {
5688 backend::encode_slice::<A, PAD>(input, output)
5689 }
5690
5691 pub fn encode_slice_wrapped(
5710 &self,
5711 input: &[u8],
5712 output: &mut [u8],
5713 wrap: LineWrap,
5714 ) -> Result<usize, EncodeError> {
5715 let required = self.wrapped_encoded_len(input.len(), wrap)?;
5716 if output.len() < required {
5717 return Err(EncodeError::OutputTooSmall {
5718 required,
5719 available: output.len(),
5720 });
5721 }
5722
5723 let encoded_len =
5724 checked_encoded_len(input.len(), PAD).ok_or(EncodeError::LengthOverflow)?;
5725 if encoded_len == 0 {
5726 return Ok(0);
5727 }
5728
5729 let combined_required = match required.checked_add(encoded_len) {
5732 Some(len) => len,
5733 None => usize::MAX,
5734 };
5735 if output.len() < combined_required {
5736 let mut scratch = [0u8; 1024];
5737 let mut input_offset = 0;
5738 let mut output_offset = 0;
5739 let mut column = 0;
5740
5741 while input_offset < input.len() {
5742 let remaining = input.len() - input_offset;
5743 let mut take = remaining.min(768);
5744 if remaining > take {
5745 take -= take % 3;
5746 }
5747 if take == 0 {
5748 take = remaining;
5749 }
5750
5751 let encoded = match self
5752 .encode_slice(&input[input_offset..input_offset + take], &mut scratch)
5753 {
5754 Ok(encoded) => encoded,
5755 Err(err) => {
5756 wipe_bytes(&mut scratch);
5757 return Err(err);
5758 }
5759 };
5760 if let Err(err) = write_wrapped_bytes(
5761 &scratch[..encoded],
5762 output,
5763 &mut output_offset,
5764 &mut column,
5765 wrap,
5766 ) {
5767 wipe_bytes(&mut scratch);
5768 return Err(err);
5769 }
5770 wipe_bytes(&mut scratch[..encoded]);
5771 input_offset += take;
5772 }
5773
5774 Ok(output_offset)
5775 } else {
5776 let encoded =
5777 self.encode_slice(input, &mut output[required..required + encoded_len])?;
5778 let mut output_offset = 0;
5779 let mut column = 0;
5780 let mut read = required;
5781 while read < required + encoded {
5782 let byte = output[read];
5783 write_wrapped_byte(byte, output, &mut output_offset, &mut column, wrap)?;
5784 read += 1;
5785 }
5786 wipe_bytes(&mut output[required..required + encoded]);
5787 Ok(output_offset)
5788 }
5789 }
5790
5791 pub fn encode_slice_wrapped_clear_tail(
5797 &self,
5798 input: &[u8],
5799 output: &mut [u8],
5800 wrap: LineWrap,
5801 ) -> Result<usize, EncodeError> {
5802 let written = match self.encode_slice_wrapped(input, output, wrap) {
5803 Ok(written) => written,
5804 Err(err) => {
5805 wipe_bytes(output);
5806 return Err(err);
5807 }
5808 };
5809 wipe_tail(output, written);
5810 Ok(written)
5811 }
5812
5813 pub fn encode_wrapped_buffer<const CAP: usize>(
5819 &self,
5820 input: &[u8],
5821 wrap: LineWrap,
5822 ) -> Result<EncodedBuffer<CAP>, EncodeError> {
5823 let mut output = EncodedBuffer::new();
5824 let written = match self.encode_slice_wrapped_clear_tail(input, &mut output.bytes, wrap) {
5825 Ok(written) => written,
5826 Err(err) => {
5827 output.clear();
5828 return Err(err);
5829 }
5830 };
5831 output.len = written;
5832 Ok(output)
5833 }
5834
5835 #[cfg(feature = "alloc")]
5837 #[must_use = "for secret-bearing payloads use encode_wrapped_secret, which returns a redacted buffer with drop-time cleanup"]
5838 pub fn encode_wrapped_vec(
5839 &self,
5840 input: &[u8],
5841 wrap: LineWrap,
5842 ) -> Result<alloc::vec::Vec<u8>, EncodeError> {
5843 let required = self.wrapped_encoded_len(input.len(), wrap)?;
5844 let mut output = alloc::vec![0; required];
5845 let written = self.encode_slice_wrapped(input, &mut output, wrap)?;
5846 output.truncate(written);
5847 Ok(output)
5848 }
5849
5850 #[cfg(feature = "alloc")]
5852 pub fn encode_wrapped_string(
5853 &self,
5854 input: &[u8],
5855 wrap: LineWrap,
5856 ) -> Result<alloc::string::String, EncodeError> {
5857 let output = self.encode_wrapped_vec(input, wrap)?;
5858 match alloc::string::String::from_utf8(output) {
5859 Ok(output) => Ok(output),
5860 Err(_) => unreachable!("base64 encoder produced non-UTF-8 output"),
5861 }
5862 }
5863
5864 #[cfg(feature = "alloc")]
5869 pub fn encode_wrapped_secret(
5870 &self,
5871 input: &[u8],
5872 wrap: LineWrap,
5873 ) -> Result<SecretBuffer, EncodeError> {
5874 self.encode_wrapped_vec(input, wrap)
5875 .map(SecretBuffer::from_vec)
5876 }
5877
5878 pub fn encode_slice_clear_tail(
5898 &self,
5899 input: &[u8],
5900 output: &mut [u8],
5901 ) -> Result<usize, EncodeError> {
5902 let written = match self.encode_slice(input, output) {
5903 Ok(written) => written,
5904 Err(err) => {
5905 wipe_bytes(output);
5906 return Err(err);
5907 }
5908 };
5909 wipe_tail(output, written);
5910 Ok(written)
5911 }
5912
5913 pub fn encode_buffer<const CAP: usize>(
5928 &self,
5929 input: &[u8],
5930 ) -> Result<EncodedBuffer<CAP>, EncodeError> {
5931 let mut output = EncodedBuffer::new();
5932 let written = match self.encode_slice_clear_tail(input, &mut output.bytes) {
5933 Ok(written) => written,
5934 Err(err) => {
5935 output.clear();
5936 return Err(err);
5937 }
5938 };
5939 output.len = written;
5940 Ok(output)
5941 }
5942
5943 #[cfg(feature = "alloc")]
5945 #[must_use = "for secret-bearing payloads use encode_secret, which returns a redacted buffer with drop-time cleanup"]
5946 pub fn encode_vec(&self, input: &[u8]) -> Result<alloc::vec::Vec<u8>, EncodeError> {
5947 let required = checked_encoded_len(input.len(), PAD).ok_or(EncodeError::LengthOverflow)?;
5948 let mut output = alloc::vec![0; required];
5949 let written = self.encode_slice(input, &mut output)?;
5950 output.truncate(written);
5951 Ok(output)
5952 }
5953
5954 #[cfg(feature = "alloc")]
5959 pub fn encode_secret(&self, input: &[u8]) -> Result<SecretBuffer, EncodeError> {
5960 self.encode_vec(input).map(SecretBuffer::from_vec)
5961 }
5962
5963 #[cfg(feature = "alloc")]
5978 pub fn encode_string(&self, input: &[u8]) -> Result<alloc::string::String, EncodeError> {
5979 let output = self.encode_vec(input)?;
5980 match alloc::string::String::from_utf8(output) {
5981 Ok(output) => Ok(output),
5982 Err(_) => unreachable!("base64 encoder produced non-UTF-8 output"),
5983 }
5984 }
5985
5986 pub fn encode_in_place<'a>(
6003 &self,
6004 buffer: &'a mut [u8],
6005 input_len: usize,
6006 ) -> Result<&'a mut [u8], EncodeError> {
6007 if input_len > buffer.len() {
6008 return Err(EncodeError::InputTooLarge {
6009 input_len,
6010 buffer_len: buffer.len(),
6011 });
6012 }
6013
6014 let required = checked_encoded_len(input_len, PAD).ok_or(EncodeError::LengthOverflow)?;
6015 if buffer.len() < required {
6016 return Err(EncodeError::OutputTooSmall {
6017 required,
6018 available: buffer.len(),
6019 });
6020 }
6021
6022 let mut read = input_len;
6023 let mut write = required;
6024
6025 match input_len % 3 {
6026 0 => {}
6027 1 => {
6028 read -= 1;
6029 let b0 = buffer[read];
6030 if PAD {
6031 write -= 4;
6032 buffer[write] = encode_base64_value_runtime::<A>(b0 >> 2);
6033 buffer[write + 1] = encode_base64_value_runtime::<A>((b0 & 0b0000_0011) << 4);
6034 buffer[write + 2] = b'=';
6035 buffer[write + 3] = b'=';
6036 } else {
6037 write -= 2;
6038 buffer[write] = encode_base64_value_runtime::<A>(b0 >> 2);
6039 buffer[write + 1] = encode_base64_value_runtime::<A>((b0 & 0b0000_0011) << 4);
6040 }
6041 }
6042 2 => {
6043 read -= 2;
6044 let b0 = buffer[read];
6045 let b1 = buffer[read + 1];
6046 if PAD {
6047 write -= 4;
6048 buffer[write] = encode_base64_value_runtime::<A>(b0 >> 2);
6049 buffer[write + 1] =
6050 encode_base64_value_runtime::<A>(((b0 & 0b0000_0011) << 4) | (b1 >> 4));
6051 buffer[write + 2] = encode_base64_value_runtime::<A>((b1 & 0b0000_1111) << 2);
6052 buffer[write + 3] = b'=';
6053 } else {
6054 write -= 3;
6055 buffer[write] = encode_base64_value_runtime::<A>(b0 >> 2);
6056 buffer[write + 1] =
6057 encode_base64_value_runtime::<A>(((b0 & 0b0000_0011) << 4) | (b1 >> 4));
6058 buffer[write + 2] = encode_base64_value_runtime::<A>((b1 & 0b0000_1111) << 2);
6059 }
6060 }
6061 _ => unreachable!(),
6062 }
6063
6064 while read > 0 {
6065 read -= 3;
6066 write -= 4;
6067 let b0 = buffer[read];
6068 let b1 = buffer[read + 1];
6069 let b2 = buffer[read + 2];
6070
6071 buffer[write] = encode_base64_value_runtime::<A>(b0 >> 2);
6072 buffer[write + 1] =
6073 encode_base64_value_runtime::<A>(((b0 & 0b0000_0011) << 4) | (b1 >> 4));
6074 buffer[write + 2] =
6075 encode_base64_value_runtime::<A>(((b1 & 0b0000_1111) << 2) | (b2 >> 6));
6076 buffer[write + 3] = encode_base64_value_runtime::<A>(b2 & 0b0011_1111);
6077 }
6078
6079 debug_assert_eq!(write, 0);
6083 Ok(&mut buffer[..required])
6084 }
6085
6086 pub fn encode_in_place_clear_tail<'a>(
6104 &self,
6105 buffer: &'a mut [u8],
6106 input_len: usize,
6107 ) -> Result<&'a mut [u8], EncodeError> {
6108 let len = match self.encode_in_place(buffer, input_len) {
6109 Ok(encoded) => encoded.len(),
6110 Err(err) => {
6111 wipe_bytes(buffer);
6112 return Err(err);
6113 }
6114 };
6115 wipe_tail(buffer, len);
6116 Ok(&mut buffer[..len])
6117 }
6118
6119 #[must_use = "handle decode errors; use crate::ct for secret-bearing payloads"]
6137 pub fn decode_slice(&self, input: &[u8], output: &mut [u8]) -> Result<usize, DecodeError> {
6138 backend::decode_slice::<A, PAD>(input, output)
6139 }
6140
6141 pub fn decode_slice_clear_tail(
6161 &self,
6162 input: &[u8],
6163 output: &mut [u8],
6164 ) -> Result<usize, DecodeError> {
6165 let written = match self.decode_slice(input, output) {
6166 Ok(written) => written,
6167 Err(err) => {
6168 wipe_bytes(output);
6169 return Err(err);
6170 }
6171 };
6172 wipe_tail(output, written);
6173 Ok(written)
6174 }
6175
6176 pub fn decode_buffer<const CAP: usize>(
6191 &self,
6192 input: &[u8],
6193 ) -> Result<DecodedBuffer<CAP>, DecodeError> {
6194 let mut output = DecodedBuffer::new();
6195 let written = match self.decode_slice_clear_tail(input, &mut output.bytes) {
6196 Ok(written) => written,
6197 Err(err) => {
6198 output.clear();
6199 return Err(err);
6200 }
6201 };
6202 output.len = written;
6203 Ok(output)
6204 }
6205
6206 #[must_use = "handle decode errors; use crate::ct for secret-bearing payloads"]
6219 pub fn decode_slice_legacy(
6220 &self,
6221 input: &[u8],
6222 output: &mut [u8],
6223 ) -> Result<usize, DecodeError> {
6224 let required = validate_legacy_decode::<A, PAD>(input)?;
6225 if output.len() < required {
6226 return Err(DecodeError::OutputTooSmall {
6227 required,
6228 available: output.len(),
6229 });
6230 }
6231 decode_legacy_to_slice::<A, PAD>(input, output)
6232 }
6233
6234 pub fn decode_slice_legacy_clear_tail(
6254 &self,
6255 input: &[u8],
6256 output: &mut [u8],
6257 ) -> Result<usize, DecodeError> {
6258 let written = match self.decode_slice_legacy(input, output) {
6259 Ok(written) => written,
6260 Err(err) => {
6261 wipe_bytes(output);
6262 return Err(err);
6263 }
6264 };
6265 wipe_tail(output, written);
6266 Ok(written)
6267 }
6268
6269 pub fn decode_buffer_legacy<const CAP: usize>(
6277 &self,
6278 input: &[u8],
6279 ) -> Result<DecodedBuffer<CAP>, DecodeError> {
6280 let mut output = DecodedBuffer::new();
6281 let written = match self.decode_slice_legacy_clear_tail(input, &mut output.bytes) {
6282 Ok(written) => written,
6283 Err(err) => {
6284 output.clear();
6285 return Err(err);
6286 }
6287 };
6288 output.len = written;
6289 Ok(output)
6290 }
6291
6292 #[must_use = "handle decode errors; use crate::ct for secret-bearing payloads"]
6306 pub fn decode_slice_wrapped(
6307 &self,
6308 input: &[u8],
6309 output: &mut [u8],
6310 wrap: LineWrap,
6311 ) -> Result<usize, DecodeError> {
6312 let required = validate_wrapped_decode::<A, PAD>(input, wrap)?;
6313 if output.len() < required {
6314 return Err(DecodeError::OutputTooSmall {
6315 required,
6316 available: output.len(),
6317 });
6318 }
6319 decode_wrapped_to_slice::<A, PAD>(input, output, wrap)
6320 }
6321
6322 pub fn decode_slice_wrapped_clear_tail(
6328 &self,
6329 input: &[u8],
6330 output: &mut [u8],
6331 wrap: LineWrap,
6332 ) -> Result<usize, DecodeError> {
6333 let written = match self.decode_slice_wrapped(input, output, wrap) {
6334 Ok(written) => written,
6335 Err(err) => {
6336 wipe_bytes(output);
6337 return Err(err);
6338 }
6339 };
6340 wipe_tail(output, written);
6341 Ok(written)
6342 }
6343
6344 pub fn decode_wrapped_buffer<const CAP: usize>(
6353 &self,
6354 input: &[u8],
6355 wrap: LineWrap,
6356 ) -> Result<DecodedBuffer<CAP>, DecodeError> {
6357 let mut output = DecodedBuffer::new();
6358 let written = match self.decode_slice_wrapped_clear_tail(input, &mut output.bytes, wrap) {
6359 Ok(written) => written,
6360 Err(err) => {
6361 output.clear();
6362 return Err(err);
6363 }
6364 };
6365 output.len = written;
6366 Ok(output)
6367 }
6368
6369 #[cfg(feature = "alloc")]
6373 #[must_use = "for secret-bearing payloads use decode_secret, which returns a redacted buffer with drop-time cleanup"]
6374 pub fn decode_vec(&self, input: &[u8]) -> Result<alloc::vec::Vec<u8>, DecodeError> {
6375 let required = validate_decode::<A, PAD>(input)?;
6376 let mut output = alloc::vec![0; required];
6377 let written = match self.decode_slice(input, &mut output) {
6378 Ok(written) => written,
6379 Err(err) => {
6380 wipe_bytes(&mut output);
6381 return Err(err);
6382 }
6383 };
6384 output.truncate(written);
6385 Ok(output)
6386 }
6387
6388 #[cfg(feature = "alloc")]
6393 pub fn decode_secret(&self, input: &[u8]) -> Result<SecretBuffer, DecodeError> {
6394 self.decode_vec(input).map(SecretBuffer::from_vec)
6395 }
6396
6397 #[cfg(feature = "alloc")]
6400 #[must_use = "for secret-bearing payloads use decode_secret_legacy, which returns a redacted buffer with drop-time cleanup"]
6401 pub fn decode_vec_legacy(&self, input: &[u8]) -> Result<alloc::vec::Vec<u8>, DecodeError> {
6402 let required = validate_legacy_decode::<A, PAD>(input)?;
6403 let mut output = alloc::vec![0; required];
6404 let written = match self.decode_slice_legacy(input, &mut output) {
6405 Ok(written) => written,
6406 Err(err) => {
6407 wipe_bytes(&mut output);
6408 return Err(err);
6409 }
6410 };
6411 output.truncate(written);
6412 Ok(output)
6413 }
6414
6415 #[cfg(feature = "alloc")]
6422 pub fn decode_secret_legacy(&self, input: &[u8]) -> Result<SecretBuffer, DecodeError> {
6423 self.decode_vec_legacy(input).map(SecretBuffer::from_vec)
6424 }
6425
6426 #[cfg(feature = "alloc")]
6428 #[must_use = "for secret-bearing payloads use decode_wrapped_secret, which returns a redacted buffer with drop-time cleanup"]
6429 pub fn decode_wrapped_vec(
6430 &self,
6431 input: &[u8],
6432 wrap: LineWrap,
6433 ) -> Result<alloc::vec::Vec<u8>, DecodeError> {
6434 let required = validate_wrapped_decode::<A, PAD>(input, wrap)?;
6435 let mut output = alloc::vec![0; required];
6436 let written = match self.decode_slice_wrapped(input, &mut output, wrap) {
6437 Ok(written) => written,
6438 Err(err) => {
6439 wipe_bytes(&mut output);
6440 return Err(err);
6441 }
6442 };
6443 output.truncate(written);
6444 Ok(output)
6445 }
6446
6447 #[cfg(feature = "alloc")]
6454 pub fn decode_wrapped_secret(
6455 &self,
6456 input: &[u8],
6457 wrap: LineWrap,
6458 ) -> Result<SecretBuffer, DecodeError> {
6459 self.decode_wrapped_vec(input, wrap)
6460 .map(SecretBuffer::from_vec)
6461 }
6462
6463 pub fn decode_in_place_wrapped<'a>(
6487 &self,
6488 buffer: &'a mut [u8],
6489 wrap: LineWrap,
6490 ) -> Result<&'a mut [u8], DecodeError> {
6491 let _required = validate_wrapped_decode::<A, PAD>(buffer, wrap)?;
6492 let compacted = compact_wrapped_input(buffer, wrap)?;
6493 let len = Self::decode_slice_to_start(&mut buffer[..compacted])?;
6494 Ok(&mut buffer[..len])
6495 }
6496
6497 pub fn decode_in_place_wrapped_clear_tail<'a>(
6518 &self,
6519 buffer: &'a mut [u8],
6520 wrap: LineWrap,
6521 ) -> Result<&'a mut [u8], DecodeError> {
6522 if let Err(err) = validate_wrapped_decode::<A, PAD>(buffer, wrap) {
6523 wipe_bytes(buffer);
6524 return Err(err);
6525 }
6526
6527 let compacted = match compact_wrapped_input(buffer, wrap) {
6528 Ok(compacted) => compacted,
6529 Err(err) => {
6530 wipe_bytes(buffer);
6531 return Err(err);
6532 }
6533 };
6534
6535 let len = match Self::decode_slice_to_start(&mut buffer[..compacted]) {
6536 Ok(len) => len,
6537 Err(err) => {
6538 wipe_bytes(buffer);
6539 return Err(err);
6540 }
6541 };
6542 wipe_tail(buffer, len);
6543 Ok(&mut buffer[..len])
6544 }
6545
6546 pub fn decode_in_place<'a>(&self, buffer: &'a mut [u8]) -> Result<&'a mut [u8], DecodeError> {
6571 let len = Self::decode_slice_to_start(buffer)?;
6572 Ok(&mut buffer[..len])
6573 }
6574
6575 pub fn decode_in_place_clear_tail<'a>(
6592 &self,
6593 buffer: &'a mut [u8],
6594 ) -> Result<&'a mut [u8], DecodeError> {
6595 let len = match Self::decode_slice_to_start(buffer) {
6596 Ok(len) => len,
6597 Err(err) => {
6598 wipe_bytes(buffer);
6599 return Err(err);
6600 }
6601 };
6602 wipe_tail(buffer, len);
6603 Ok(&mut buffer[..len])
6604 }
6605
6606 pub fn decode_in_place_legacy<'a>(
6614 &self,
6615 buffer: &'a mut [u8],
6616 ) -> Result<&'a mut [u8], DecodeError> {
6617 let _required = validate_legacy_decode::<A, PAD>(buffer)?;
6618 let mut write = 0;
6619 let mut read = 0;
6620 while read < buffer.len() {
6621 let byte = buffer[read];
6622 if !is_legacy_whitespace(byte) {
6623 buffer[write] = byte;
6624 write += 1;
6625 }
6626 read += 1;
6627 }
6628 let len = Self::decode_slice_to_start(&mut buffer[..write])?;
6629 Ok(&mut buffer[..len])
6630 }
6631
6632 pub fn decode_in_place_legacy_clear_tail<'a>(
6638 &self,
6639 buffer: &'a mut [u8],
6640 ) -> Result<&'a mut [u8], DecodeError> {
6641 if let Err(err) = validate_legacy_decode::<A, PAD>(buffer) {
6642 wipe_bytes(buffer);
6643 return Err(err);
6644 }
6645
6646 let mut write = 0;
6647 let mut read = 0;
6648 while read < buffer.len() {
6649 let byte = buffer[read];
6650 if !is_legacy_whitespace(byte) {
6651 buffer[write] = byte;
6652 write += 1;
6653 }
6654 read += 1;
6655 }
6656
6657 let len = match Self::decode_slice_to_start(&mut buffer[..write]) {
6658 Ok(len) => len,
6659 Err(err) => {
6660 wipe_bytes(buffer);
6661 return Err(err);
6662 }
6663 };
6664 wipe_tail(buffer, len);
6665 Ok(&mut buffer[..len])
6666 }
6667
6668 fn decode_slice_to_start(buffer: &mut [u8]) -> Result<usize, DecodeError> {
6669 let _required = validate_decode::<A, PAD>(buffer)?;
6670 let input_len = buffer.len();
6671 let mut read = 0;
6672 let mut write = 0;
6673 while read + 4 <= input_len {
6674 let chunk = read_quad(buffer, read)?;
6675 let available = buffer.len();
6676 let output_tail = buffer.get_mut(write..).ok_or(DecodeError::OutputTooSmall {
6677 required: write,
6678 available,
6679 })?;
6680 let written = decode_chunk::<A, PAD>(chunk, output_tail)
6681 .map_err(|err| err.with_index_offset(read))?;
6682 read += 4;
6683 write += written;
6684 if written < 3 {
6685 if read != input_len {
6686 return Err(DecodeError::InvalidPadding { index: read - 4 });
6687 }
6688 return Ok(write);
6689 }
6690 }
6691
6692 let rem = input_len - read;
6693 if rem == 0 {
6694 return Ok(write);
6695 }
6696 if PAD {
6697 return Err(DecodeError::InvalidLength);
6698 }
6699 let mut tail = [0u8; 3];
6700 tail[..rem].copy_from_slice(&buffer[read..input_len]);
6701 decode_tail_unpadded::<A>(&tail[..rem], &mut buffer[write..])
6702 .map_err(|err| err.with_index_offset(read))
6703 .map(|n| write + n)
6704 }
6705}
6706
6707fn write_wrapped_bytes(
6708 input: &[u8],
6709 output: &mut [u8],
6710 output_offset: &mut usize,
6711 column: &mut usize,
6712 wrap: LineWrap,
6713) -> Result<(), EncodeError> {
6714 for byte in input {
6715 write_wrapped_byte(*byte, output, output_offset, column, wrap)?;
6716 }
6717 Ok(())
6718}
6719
6720fn write_wrapped_byte(
6721 byte: u8,
6722 output: &mut [u8],
6723 output_offset: &mut usize,
6724 column: &mut usize,
6725 wrap: LineWrap,
6726) -> Result<(), EncodeError> {
6727 if *column == wrap.line_len {
6728 let line_ending = wrap.line_ending.as_bytes();
6729 let mut index = 0;
6730 while index < line_ending.len() {
6731 if *output_offset >= output.len() {
6732 return Err(EncodeError::OutputTooSmall {
6733 required: *output_offset + 1,
6734 available: output.len(),
6735 });
6736 }
6737 output[*output_offset] = line_ending[index];
6738 *output_offset += 1;
6739 index += 1;
6740 }
6741 *column = 0;
6742 }
6743
6744 if *output_offset >= output.len() {
6745 return Err(EncodeError::OutputTooSmall {
6746 required: *output_offset + 1,
6747 available: output.len(),
6748 });
6749 }
6750 output[*output_offset] = byte;
6751 *output_offset += 1;
6752 *column += 1;
6753 Ok(())
6754}
6755
6756#[derive(Clone, Copy, Debug, Eq, PartialEq)]
6758pub enum EncodeError {
6759 LengthOverflow,
6761 InvalidLineWrap {
6763 line_len: usize,
6765 },
6766 InputTooLarge {
6768 input_len: usize,
6770 buffer_len: usize,
6772 },
6773 OutputTooSmall {
6775 required: usize,
6777 available: usize,
6779 },
6780}
6781
6782impl core::fmt::Display for EncodeError {
6783 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
6784 match self {
6785 Self::LengthOverflow => f.write_str("base64 output length overflows usize"),
6786 Self::InvalidLineWrap { line_len } => {
6787 write!(f, "base64 line wrap length {line_len} is invalid")
6788 }
6789 Self::InputTooLarge {
6790 input_len,
6791 buffer_len,
6792 } => write!(
6793 f,
6794 "base64 input length {input_len} exceeds buffer length {buffer_len}"
6795 ),
6796 Self::OutputTooSmall {
6797 required,
6798 available,
6799 } => write!(
6800 f,
6801 "base64 output buffer too small: required {required}, available {available}"
6802 ),
6803 }
6804 }
6805}
6806
6807#[cfg(feature = "std")]
6808impl std::error::Error for EncodeError {}
6809
6810#[derive(Clone, Copy, Debug, Eq, PartialEq)]
6812pub enum AlphabetError {
6813 InvalidByte {
6815 index: usize,
6817 byte: u8,
6819 },
6820 PaddingByte {
6822 index: usize,
6824 },
6825 DuplicateByte {
6827 first: usize,
6829 second: usize,
6831 byte: u8,
6833 },
6834}
6835
6836impl core::fmt::Display for AlphabetError {
6837 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
6838 match self {
6839 Self::InvalidByte { index, byte } => {
6840 write!(
6841 f,
6842 "invalid base64 alphabet byte 0x{byte:02x} at index {index}"
6843 )
6844 }
6845 Self::PaddingByte { index } => {
6846 write!(f, "base64 alphabet contains padding byte at index {index}")
6847 }
6848 Self::DuplicateByte {
6849 first,
6850 second,
6851 byte,
6852 } => write!(
6853 f,
6854 "base64 alphabet byte 0x{byte:02x} is duplicated at indexes {first} and {second}"
6855 ),
6856 }
6857 }
6858}
6859
6860#[cfg(feature = "std")]
6861impl std::error::Error for AlphabetError {}
6862
6863#[derive(Clone, Copy, Debug, Eq, PartialEq)]
6865pub enum DecodeError {
6866 InvalidInput,
6869 InvalidLength,
6871 InvalidByte {
6873 index: usize,
6875 byte: u8,
6877 },
6878 InvalidPadding {
6880 index: usize,
6882 },
6883 InvalidLineWrap {
6885 index: usize,
6887 },
6888 OutputTooSmall {
6890 required: usize,
6892 available: usize,
6894 },
6895}
6896
6897impl core::fmt::Display for DecodeError {
6898 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
6899 match self {
6900 Self::InvalidInput => f.write_str("malformed base64 input"),
6901 Self::InvalidLength => f.write_str("invalid base64 input length"),
6902 Self::InvalidByte { index, byte } => {
6903 write!(f, "invalid base64 byte 0x{byte:02x} at index {index}")
6904 }
6905 Self::InvalidPadding { index } => write!(f, "invalid base64 padding at index {index}"),
6906 Self::InvalidLineWrap { index } => {
6907 write!(f, "invalid base64 line wrapping at index {index}")
6908 }
6909 Self::OutputTooSmall {
6910 required,
6911 available,
6912 } => write!(
6913 f,
6914 "base64 decode output buffer too small: required {required}, available {available}"
6915 ),
6916 }
6917 }
6918}
6919
6920impl DecodeError {
6921 fn with_index_offset(self, offset: usize) -> Self {
6922 match self {
6923 Self::InvalidByte { index, byte } => Self::InvalidByte {
6924 index: index + offset,
6925 byte,
6926 },
6927 Self::InvalidPadding { index } => Self::InvalidPadding {
6928 index: index + offset,
6929 },
6930 Self::InvalidLineWrap { index } => Self::InvalidLineWrap {
6931 index: index + offset,
6932 },
6933 Self::InvalidInput | Self::InvalidLength | Self::OutputTooSmall { .. } => self,
6934 }
6935 }
6936}
6937
6938#[cfg(feature = "std")]
6939impl std::error::Error for DecodeError {}
6940
6941fn validate_legacy_decode<A: Alphabet, const PAD: bool>(
6942 input: &[u8],
6943) -> Result<usize, DecodeError> {
6944 let mut chunk = [0u8; 4];
6945 let mut indexes = [0usize; 4];
6946 let mut chunk_len = 0;
6947 let mut required = 0;
6948 let mut terminal_seen = false;
6949
6950 for (index, byte) in input.iter().copied().enumerate() {
6951 if is_legacy_whitespace(byte) {
6952 continue;
6953 }
6954 if terminal_seen {
6955 return Err(DecodeError::InvalidPadding { index });
6956 }
6957
6958 chunk[chunk_len] = byte;
6959 indexes[chunk_len] = index;
6960 chunk_len += 1;
6961
6962 if chunk_len == 4 {
6963 let written =
6964 validate_chunk::<A, PAD>(chunk).map_err(|err| map_chunk_error(err, &indexes))?;
6965 required += written;
6966 terminal_seen = written < 3;
6967 chunk_len = 0;
6968 }
6969 }
6970
6971 if chunk_len == 0 {
6972 return Ok(required);
6973 }
6974 if PAD {
6975 return Err(DecodeError::InvalidLength);
6976 }
6977
6978 validate_tail_unpadded::<A>(&chunk[..chunk_len])
6979 .map_err(|err| map_partial_chunk_error(err, &indexes, chunk_len))?;
6980 Ok(required + decoded_capacity(chunk_len))
6981}
6982
6983fn decode_legacy_to_slice<A: Alphabet, const PAD: bool>(
6984 input: &[u8],
6985 output: &mut [u8],
6986) -> Result<usize, DecodeError> {
6987 let mut chunk = [0u8; 4];
6988 let mut indexes = [0usize; 4];
6989 let mut chunk_len = 0;
6990 let mut write = 0;
6991 let mut terminal_seen = false;
6992
6993 for (index, byte) in input.iter().copied().enumerate() {
6994 if is_legacy_whitespace(byte) {
6995 continue;
6996 }
6997 if terminal_seen {
6998 return Err(DecodeError::InvalidPadding { index });
6999 }
7000
7001 chunk[chunk_len] = byte;
7002 indexes[chunk_len] = index;
7003 chunk_len += 1;
7004
7005 if chunk_len == 4 {
7006 let available = output.len();
7007 let output_tail = output.get_mut(write..).ok_or(DecodeError::OutputTooSmall {
7008 required: write,
7009 available,
7010 })?;
7011 let written = decode_chunk::<A, PAD>(chunk, output_tail)
7012 .map_err(|err| map_chunk_error(err, &indexes))?;
7013 write += written;
7014 terminal_seen = written < 3;
7015 chunk_len = 0;
7016 }
7017 }
7018
7019 if chunk_len == 0 {
7020 return Ok(write);
7021 }
7022 if PAD {
7023 return Err(DecodeError::InvalidLength);
7024 }
7025
7026 decode_tail_unpadded::<A>(&chunk[..chunk_len], &mut output[write..])
7027 .map_err(|err| map_partial_chunk_error(err, &indexes, chunk_len))
7028 .map(|n| write + n)
7029}
7030
7031struct WrappedBytes<'a> {
7032 input: &'a [u8],
7033 wrap: LineWrap,
7034 index: usize,
7035 line_len: usize,
7036}
7037
7038impl<'a> WrappedBytes<'a> {
7039 const fn new(input: &'a [u8], wrap: LineWrap) -> Result<Self, DecodeError> {
7040 if wrap.line_len == 0 {
7041 return Err(DecodeError::InvalidLineWrap { index: 0 });
7042 }
7043 Ok(Self {
7044 input,
7045 wrap,
7046 index: 0,
7047 line_len: 0,
7048 })
7049 }
7050
7051 fn next_byte(&mut self) -> Result<Option<(usize, u8)>, DecodeError> {
7052 loop {
7053 if self.index == self.input.len() {
7054 return Ok(None);
7055 }
7056
7057 if self.starts_with_line_ending() {
7058 let line_end_index = self.index;
7059 if self.line_len == 0 {
7060 return Err(DecodeError::InvalidLineWrap {
7061 index: line_end_index,
7062 });
7063 }
7064
7065 self.index += self.wrap.line_ending.byte_len();
7066 if self.index == self.input.len() {
7067 self.line_len = 0;
7068 return Ok(None);
7069 }
7070
7071 if self.line_len != self.wrap.line_len {
7072 return Err(DecodeError::InvalidLineWrap {
7073 index: line_end_index,
7074 });
7075 }
7076 self.line_len = 0;
7077 continue;
7078 }
7079
7080 let byte = self.input[self.index];
7081 if matches!(byte, b'\r' | b'\n') {
7082 return Err(DecodeError::InvalidLineWrap { index: self.index });
7083 }
7084
7085 self.line_len += 1;
7086 if self.line_len > self.wrap.line_len {
7087 return Err(DecodeError::InvalidLineWrap { index: self.index });
7088 }
7089
7090 let index = self.index;
7091 self.index += 1;
7092 return Ok(Some((index, byte)));
7093 }
7094 }
7095
7096 fn starts_with_line_ending(&self) -> bool {
7097 let line_ending = self.wrap.line_ending.as_bytes();
7098 let end = self.index + line_ending.len();
7099 end <= self.input.len() && &self.input[self.index..end] == line_ending
7100 }
7101}
7102
7103fn validate_wrapped_decode<A: Alphabet, const PAD: bool>(
7104 input: &[u8],
7105 wrap: LineWrap,
7106) -> Result<usize, DecodeError> {
7107 let mut bytes = WrappedBytes::new(input, wrap)?;
7108 let mut chunk = [0u8; 4];
7109 let mut indexes = [0usize; 4];
7110 let mut chunk_len = 0;
7111 let mut required = 0;
7112 let mut terminal_seen = false;
7113
7114 while let Some((index, byte)) = bytes.next_byte()? {
7115 if terminal_seen {
7116 return Err(DecodeError::InvalidPadding { index });
7117 }
7118
7119 chunk[chunk_len] = byte;
7120 indexes[chunk_len] = index;
7121 chunk_len += 1;
7122
7123 if chunk_len == 4 {
7124 let written =
7125 validate_chunk::<A, PAD>(chunk).map_err(|err| map_chunk_error(err, &indexes))?;
7126 required += written;
7127 terminal_seen = written < 3;
7128 chunk_len = 0;
7129 }
7130 }
7131
7132 if chunk_len == 0 {
7133 return Ok(required);
7134 }
7135 if PAD {
7136 return Err(DecodeError::InvalidLength);
7137 }
7138
7139 validate_tail_unpadded::<A>(&chunk[..chunk_len])
7140 .map_err(|err| map_partial_chunk_error(err, &indexes, chunk_len))?;
7141 Ok(required + decoded_capacity(chunk_len))
7142}
7143
7144fn decode_wrapped_to_slice<A: Alphabet, const PAD: bool>(
7145 input: &[u8],
7146 output: &mut [u8],
7147 wrap: LineWrap,
7148) -> Result<usize, DecodeError> {
7149 let mut bytes = WrappedBytes::new(input, wrap)?;
7150 let mut chunk = [0u8; 4];
7151 let mut indexes = [0usize; 4];
7152 let mut chunk_len = 0;
7153 let mut write = 0;
7154 let mut terminal_seen = false;
7155
7156 while let Some((index, byte)) = bytes.next_byte()? {
7157 if terminal_seen {
7158 return Err(DecodeError::InvalidPadding { index });
7159 }
7160
7161 chunk[chunk_len] = byte;
7162 indexes[chunk_len] = index;
7163 chunk_len += 1;
7164
7165 if chunk_len == 4 {
7166 let available = output.len();
7167 let output_tail = output.get_mut(write..).ok_or(DecodeError::OutputTooSmall {
7168 required: write,
7169 available,
7170 })?;
7171 let written = decode_chunk::<A, PAD>(chunk, output_tail)
7172 .map_err(|err| map_chunk_error(err, &indexes))?;
7173 write += written;
7174 terminal_seen = written < 3;
7175 chunk_len = 0;
7176 }
7177 }
7178
7179 if chunk_len == 0 {
7180 return Ok(write);
7181 }
7182 if PAD {
7183 return Err(DecodeError::InvalidLength);
7184 }
7185
7186 decode_tail_unpadded::<A>(&chunk[..chunk_len], &mut output[write..])
7187 .map_err(|err| map_partial_chunk_error(err, &indexes, chunk_len))
7188 .map(|n| write + n)
7189}
7190
7191fn compact_wrapped_input(buffer: &mut [u8], wrap: LineWrap) -> Result<usize, DecodeError> {
7192 if !wrap.is_valid() {
7193 return Err(DecodeError::InvalidLineWrap { index: 0 });
7194 }
7195
7196 let line_ending = wrap.line_ending.as_bytes();
7197 let line_ending_len = line_ending.len();
7198 let mut read = 0;
7199 let mut write = 0;
7200
7201 while read < buffer.len() {
7202 let line_end = read + line_ending_len;
7203 if buffer.get(read..line_end) == Some(line_ending) {
7204 read = line_end;
7205 continue;
7206 }
7207
7208 buffer[write] = buffer[read];
7209 write += 1;
7210 read += 1;
7211 }
7212
7213 Ok(write)
7214}
7215
7216#[inline]
7217const fn is_legacy_whitespace(byte: u8) -> bool {
7218 matches!(byte, b' ' | b'\t' | b'\r' | b'\n')
7219}
7220
7221fn map_chunk_error(err: DecodeError, indexes: &[usize; 4]) -> DecodeError {
7222 match err {
7223 DecodeError::InvalidByte { index, byte } => DecodeError::InvalidByte {
7224 index: indexes[index],
7225 byte,
7226 },
7227 DecodeError::InvalidPadding { index } => DecodeError::InvalidPadding {
7228 index: indexes[index],
7229 },
7230 DecodeError::InvalidInput
7231 | DecodeError::InvalidLineWrap { .. }
7232 | DecodeError::InvalidLength
7233 | DecodeError::OutputTooSmall { .. } => err,
7234 }
7235}
7236
7237fn map_partial_chunk_error(err: DecodeError, indexes: &[usize; 4], len: usize) -> DecodeError {
7238 match err {
7239 DecodeError::InvalidByte { index, byte } if index < len => DecodeError::InvalidByte {
7240 index: indexes[index],
7241 byte,
7242 },
7243 DecodeError::InvalidPadding { index } if index < len => DecodeError::InvalidPadding {
7244 index: indexes[index],
7245 },
7246 DecodeError::InvalidByte { .. }
7247 | DecodeError::InvalidPadding { .. }
7248 | DecodeError::InvalidLineWrap { .. }
7249 | DecodeError::InvalidInput
7250 | DecodeError::InvalidLength
7251 | DecodeError::OutputTooSmall { .. } => err,
7252 }
7253}
7254
7255fn decode_padded<A: Alphabet>(input: &[u8], output: &mut [u8]) -> Result<usize, DecodeError> {
7256 if !input.len().is_multiple_of(4) {
7257 return Err(DecodeError::InvalidLength);
7258 }
7259 let required = decoded_len_padded(input)?;
7260 if output.len() < required {
7261 return Err(DecodeError::OutputTooSmall {
7262 required,
7263 available: output.len(),
7264 });
7265 }
7266
7267 let mut read = 0;
7268 let mut write = 0;
7269 while read < input.len() {
7270 let chunk = read_quad(input, read)?;
7271 let available = output.len();
7272 let output_tail = output.get_mut(write..).ok_or(DecodeError::OutputTooSmall {
7273 required: write,
7274 available,
7275 })?;
7276 let written = decode_chunk::<A, true>(chunk, output_tail)
7277 .map_err(|err| err.with_index_offset(read))?;
7278 read += 4;
7279 write += written;
7280 if written < 3 && read != input.len() {
7281 return Err(DecodeError::InvalidPadding { index: read - 4 });
7282 }
7283 }
7284 Ok(write)
7285}
7286
7287fn validate_decode<A: Alphabet, const PAD: bool>(input: &[u8]) -> Result<usize, DecodeError> {
7288 if input.is_empty() {
7289 return Ok(0);
7290 }
7291
7292 if PAD {
7293 validate_padded::<A>(input)
7294 } else {
7295 validate_unpadded::<A>(input)
7296 }
7297}
7298
7299fn validate_padded<A: Alphabet>(input: &[u8]) -> Result<usize, DecodeError> {
7300 if !input.len().is_multiple_of(4) {
7301 return Err(DecodeError::InvalidLength);
7302 }
7303 let required = decoded_len_padded(input)?;
7304
7305 let mut read = 0;
7306 while read < input.len() {
7307 let chunk = read_quad(input, read)?;
7308 let written =
7309 validate_chunk::<A, true>(chunk).map_err(|err| err.with_index_offset(read))?;
7310 read += 4;
7311 if written < 3 && read != input.len() {
7312 return Err(DecodeError::InvalidPadding { index: read - 4 });
7313 }
7314 }
7315
7316 Ok(required)
7317}
7318
7319fn validate_unpadded<A: Alphabet>(input: &[u8]) -> Result<usize, DecodeError> {
7320 let required = decoded_len_unpadded(input)?;
7321
7322 let mut read = 0;
7323 while read + 4 <= input.len() {
7324 let chunk = read_quad(input, read)?;
7325 validate_chunk::<A, false>(chunk).map_err(|err| err.with_index_offset(read))?;
7326 read += 4;
7327 }
7328 validate_tail_unpadded::<A>(&input[read..]).map_err(|err| err.with_index_offset(read))?;
7329
7330 Ok(required)
7331}
7332
7333fn decode_unpadded<A: Alphabet>(input: &[u8], output: &mut [u8]) -> Result<usize, DecodeError> {
7334 let required = decoded_len_unpadded(input)?;
7335 if output.len() < required {
7336 return Err(DecodeError::OutputTooSmall {
7337 required,
7338 available: output.len(),
7339 });
7340 }
7341
7342 let mut read = 0;
7343 let mut write = 0;
7344 while read + 4 <= input.len() {
7345 let chunk = read_quad(input, read)?;
7346 let available = output.len();
7347 let output_tail = output.get_mut(write..).ok_or(DecodeError::OutputTooSmall {
7348 required: write,
7349 available,
7350 })?;
7351 let written = decode_chunk::<A, false>(chunk, output_tail)
7352 .map_err(|err| err.with_index_offset(read))?;
7353 read += 4;
7354 write += written;
7355 }
7356 decode_tail_unpadded::<A>(&input[read..], &mut output[write..])
7357 .map_err(|err| err.with_index_offset(read))
7358 .map(|n| write + n)
7359}
7360
7361fn decoded_len_padded(input: &[u8]) -> Result<usize, DecodeError> {
7362 if input.is_empty() {
7363 return Ok(0);
7364 }
7365 if !input.len().is_multiple_of(4) {
7366 return Err(DecodeError::InvalidLength);
7367 }
7368
7369 let Some((&last, before_last_prefix)) = input.split_last() else {
7370 return Ok(0);
7371 };
7372 let Some(&before_last) = before_last_prefix.last() else {
7373 return Err(DecodeError::InvalidLength);
7374 };
7375
7376 let mut padding = 0;
7377 if last == b'=' {
7378 padding += 1;
7379 }
7380 if before_last == b'=' {
7381 padding += 1;
7382 }
7383 if padding == 0
7384 && let Some(index) = input.iter().position(|byte| *byte == b'=')
7385 {
7386 return Err(DecodeError::InvalidPadding { index });
7387 }
7388 if padding > 0 {
7389 let first_pad = input.len() - padding;
7390 if let Some(index) = input[..first_pad].iter().position(|byte| *byte == b'=') {
7391 return Err(DecodeError::InvalidPadding { index });
7392 }
7393 }
7394 Ok(input.len() / 4 * 3 - padding)
7395}
7396
7397fn decoded_len_unpadded(input: &[u8]) -> Result<usize, DecodeError> {
7398 if input.len() % 4 == 1 {
7399 return Err(DecodeError::InvalidLength);
7400 }
7401 if let Some(index) = input.iter().position(|byte| *byte == b'=') {
7402 return Err(DecodeError::InvalidPadding { index });
7403 }
7404 Ok(decoded_capacity(input.len()))
7405}
7406
7407fn read_quad(input: &[u8], offset: usize) -> Result<[u8; 4], DecodeError> {
7408 let end = offset.checked_add(4).ok_or(DecodeError::InvalidLength)?;
7409 match input.get(offset..end) {
7410 Some([b0, b1, b2, b3]) => Ok([*b0, *b1, *b2, *b3]),
7411 _ => Err(DecodeError::InvalidLength),
7412 }
7413}
7414
7415fn first_padding_index_unchecked(input: [u8; 4]) -> usize {
7416 let [b0, b1, b2, b3] = input;
7417 if b0 == b'=' {
7418 0
7419 } else if b1 == b'=' {
7420 1
7421 } else if b2 == b'=' {
7422 2
7423 } else if b3 == b'=' {
7424 3
7425 } else {
7426 debug_assert!(
7427 false,
7428 "first_padding_index_unchecked called with no padding"
7429 );
7430 4
7431 }
7432}
7433
7434fn validate_chunk<A: Alphabet, const PAD: bool>(input: [u8; 4]) -> Result<usize, DecodeError> {
7435 let [b0, b1, b2, b3] = input;
7436 let _v0 = decode_byte::<A>(b0, 0)?;
7437 let v1 = decode_byte::<A>(b1, 1)?;
7438
7439 match (b2, b3) {
7440 (b'=', b'=') if PAD => {
7441 if v1 & 0b0000_1111 != 0 {
7442 return Err(DecodeError::InvalidPadding { index: 1 });
7443 }
7444 Ok(1)
7445 }
7446 (b'=', _) if PAD => Err(DecodeError::InvalidPadding { index: 2 }),
7447 (_, b'=') if PAD => {
7448 let v2 = decode_byte::<A>(b2, 2)?;
7449 if v2 & 0b0000_0011 != 0 {
7450 return Err(DecodeError::InvalidPadding { index: 2 });
7451 }
7452 Ok(2)
7453 }
7454 (b'=', _) | (_, b'=') => Err(DecodeError::InvalidPadding {
7455 index: first_padding_index_unchecked(input),
7456 }),
7457 _ => {
7458 decode_byte::<A>(b2, 2)?;
7459 decode_byte::<A>(b3, 3)?;
7460 Ok(3)
7461 }
7462 }
7463}
7464
7465fn decode_chunk<A: Alphabet, const PAD: bool>(
7466 input: [u8; 4],
7467 output: &mut [u8],
7468) -> Result<usize, DecodeError> {
7469 let [b0, b1, b2, b3] = input;
7470 let v0 = decode_byte::<A>(b0, 0)?;
7471 let v1 = decode_byte::<A>(b1, 1)?;
7472
7473 match (b2, b3) {
7474 (b'=', b'=') if PAD => {
7475 if output.is_empty() {
7476 return Err(DecodeError::OutputTooSmall {
7477 required: 1,
7478 available: output.len(),
7479 });
7480 }
7481 if v1 & 0b0000_1111 != 0 {
7482 return Err(DecodeError::InvalidPadding { index: 1 });
7483 }
7484 output[0] = (v0 << 2) | (v1 >> 4);
7485 Ok(1)
7486 }
7487 (b'=', _) if PAD => Err(DecodeError::InvalidPadding { index: 2 }),
7488 (_, b'=') if PAD => {
7489 if output.len() < 2 {
7490 return Err(DecodeError::OutputTooSmall {
7491 required: 2,
7492 available: output.len(),
7493 });
7494 }
7495 let v2 = decode_byte::<A>(b2, 2)?;
7496 if v2 & 0b0000_0011 != 0 {
7497 return Err(DecodeError::InvalidPadding { index: 2 });
7498 }
7499 output[0] = (v0 << 2) | (v1 >> 4);
7500 output[1] = (v1 << 4) | (v2 >> 2);
7501 Ok(2)
7502 }
7503 (b'=', _) | (_, b'=') => Err(DecodeError::InvalidPadding {
7504 index: first_padding_index_unchecked(input),
7505 }),
7506 _ => {
7507 if output.len() < 3 {
7508 return Err(DecodeError::OutputTooSmall {
7509 required: 3,
7510 available: output.len(),
7511 });
7512 }
7513 let v2 = decode_byte::<A>(b2, 2)?;
7514 let v3 = decode_byte::<A>(b3, 3)?;
7515 output[0] = (v0 << 2) | (v1 >> 4);
7516 output[1] = (v1 << 4) | (v2 >> 2);
7517 output[2] = (v2 << 6) | v3;
7518 Ok(3)
7519 }
7520 }
7521}
7522
7523fn validate_tail_unpadded<A: Alphabet>(input: &[u8]) -> Result<(), DecodeError> {
7524 match input {
7525 [] => Ok(()),
7526 [b0, b1] => {
7527 decode_byte::<A>(*b0, 0)?;
7528 let v1 = decode_byte::<A>(*b1, 1)?;
7529 if v1 & 0b0000_1111 != 0 {
7530 return Err(DecodeError::InvalidPadding { index: 1 });
7531 }
7532 Ok(())
7533 }
7534 [b0, b1, b2] => {
7535 decode_byte::<A>(*b0, 0)?;
7536 decode_byte::<A>(*b1, 1)?;
7537 let v2 = decode_byte::<A>(*b2, 2)?;
7538 if v2 & 0b0000_0011 != 0 {
7539 return Err(DecodeError::InvalidPadding { index: 2 });
7540 }
7541 Ok(())
7542 }
7543 _ => Err(DecodeError::InvalidLength),
7544 }
7545}
7546
7547fn decode_tail_unpadded<A: Alphabet>(
7548 input: &[u8],
7549 output: &mut [u8],
7550) -> Result<usize, DecodeError> {
7551 match input {
7552 [] => Ok(0),
7553 [b0, b1] => {
7554 let Some(out0) = output.first_mut() else {
7555 return Err(DecodeError::OutputTooSmall {
7556 required: 1,
7557 available: output.len(),
7558 });
7559 };
7560 let v0 = decode_byte::<A>(*b0, 0)?;
7561 let v1 = decode_byte::<A>(*b1, 1)?;
7562 if v1 & 0b0000_1111 != 0 {
7563 return Err(DecodeError::InvalidPadding { index: 1 });
7564 }
7565 *out0 = (v0 << 2) | (v1 >> 4);
7566 Ok(1)
7567 }
7568 [b0, b1, b2] => {
7569 let available = output.len();
7570 let Some([out0, out1]) = output.get_mut(..2) else {
7571 return Err(DecodeError::OutputTooSmall {
7572 required: 2,
7573 available,
7574 });
7575 };
7576 let v0 = decode_byte::<A>(*b0, 0)?;
7577 let v1 = decode_byte::<A>(*b1, 1)?;
7578 let v2 = decode_byte::<A>(*b2, 2)?;
7579 if v2 & 0b0000_0011 != 0 {
7580 return Err(DecodeError::InvalidPadding { index: 2 });
7581 }
7582 *out0 = (v0 << 2) | (v1 >> 4);
7583 *out1 = (v1 << 4) | (v2 >> 2);
7584 Ok(2)
7585 }
7586 _ => Err(DecodeError::InvalidLength),
7587 }
7588}
7589
7590fn decode_byte<A: Alphabet>(byte: u8, index: usize) -> Result<u8, DecodeError> {
7591 A::decode(byte).ok_or(DecodeError::InvalidByte { index, byte })
7592}
7593
7594fn ct_decode_slice<A: Alphabet, const PAD: bool>(
7595 input: &[u8],
7596 output: &mut [u8],
7597) -> Result<usize, DecodeError> {
7598 if input.is_empty() {
7599 return Ok(0);
7600 }
7601
7602 if PAD {
7603 ct_decode_padded::<A>(input, output)
7604 } else {
7605 ct_decode_unpadded::<A>(input, output)
7606 }
7607}
7608
7609fn ct_decode_slice_staged_clear_tail<A: Alphabet, const PAD: bool>(
7610 input: &[u8],
7611 output: &mut [u8],
7612 staging: &mut [u8],
7613) -> Result<usize, DecodeError> {
7614 let required = match ct_decoded_len::<A, PAD>(input) {
7615 Ok(required) => required,
7616 Err(err) => {
7617 wipe_bytes(output);
7618 wipe_bytes(staging);
7619 return Err(err);
7620 }
7621 };
7622
7623 if output.len() < required {
7624 wipe_bytes(output);
7625 wipe_bytes(staging);
7626 return Err(DecodeError::OutputTooSmall {
7627 required,
7628 available: output.len(),
7629 });
7630 }
7631
7632 if staging.len() < required {
7633 wipe_bytes(output);
7634 wipe_bytes(staging);
7635 return Err(DecodeError::OutputTooSmall {
7636 required,
7637 available: staging.len(),
7638 });
7639 }
7640
7641 let written = match ct_decode_slice::<A, PAD>(input, &mut staging[..required]) {
7642 Ok(written) => written,
7643 Err(err) => {
7644 wipe_bytes(output);
7645 wipe_bytes(staging);
7646 return Err(err);
7647 }
7648 };
7649
7650 output[..written].copy_from_slice(&staging[..written]);
7651 wipe_bytes(staging);
7652 wipe_tail(output, written);
7653 Ok(written)
7654}
7655
7656fn ct_decode_in_place<A: Alphabet, const PAD: bool>(
7657 buffer: &mut [u8],
7658) -> Result<usize, DecodeError> {
7659 if buffer.is_empty() {
7660 return Ok(0);
7661 }
7662
7663 if PAD {
7664 ct_decode_padded_in_place::<A>(buffer)
7665 } else {
7666 ct_decode_unpadded_in_place::<A>(buffer)
7667 }
7668}
7669
7670#[inline(never)]
7671#[allow(unsafe_code)]
7672fn ct_error_gate_barrier(invalid_byte: u8, invalid_padding: u8) {
7673 core::hint::black_box(invalid_byte | invalid_padding);
7674 core::sync::atomic::compiler_fence(core::sync::atomic::Ordering::SeqCst);
7675
7676 #[cfg(all(not(miri), any(target_arch = "x86", target_arch = "x86_64")))]
7677 {
7678 unsafe {
7681 core::arch::asm!("lfence", options(nostack, preserves_flags, nomem));
7682 }
7683 }
7684
7685 #[cfg(all(not(miri), target_arch = "aarch64"))]
7686 {
7687 unsafe {
7691 core::arch::asm!("isb sy", "hint #20", options(nostack, preserves_flags));
7692 }
7693 }
7694
7695 #[cfg(all(not(miri), target_arch = "arm"))]
7696 {
7697 unsafe {
7700 core::arch::asm!("isb sy", options(nostack, preserves_flags));
7701 }
7702 }
7703
7704 #[cfg(all(not(miri), any(target_arch = "riscv32", target_arch = "riscv64")))]
7705 {
7706 unsafe {
7711 core::arch::asm!("fence rw, rw", options(nostack, preserves_flags));
7712 }
7713 }
7714}
7715
7716fn ct_validate_decode<A: Alphabet, const PAD: bool>(input: &[u8]) -> Result<(), DecodeError> {
7717 if input.is_empty() {
7718 return Ok(());
7719 }
7720
7721 if PAD {
7722 ct_validate_padded::<A>(input)
7723 } else {
7724 ct_validate_unpadded::<A>(input)
7725 }
7726}
7727
7728fn ct_decoded_len<A: Alphabet, const PAD: bool>(input: &[u8]) -> Result<usize, DecodeError> {
7729 ct_validate_decode::<A, PAD>(input)?;
7730 if input.is_empty() {
7731 return Ok(0);
7732 }
7733
7734 if PAD {
7735 Ok(input.len() / 4 * 3 - ct_padding_len(input))
7736 } else {
7737 let full_quads = input.len() / 4 * 3;
7738 match input.len() % 4 {
7739 0 => Ok(full_quads),
7740 2 => Ok(full_quads + 1),
7741 3 => Ok(full_quads + 2),
7742 _ => Err(DecodeError::InvalidLength),
7743 }
7744 }
7745}
7746
7747fn ct_validate_padded<A: Alphabet>(input: &[u8]) -> Result<(), DecodeError> {
7748 if !input.len().is_multiple_of(4) {
7749 return Err(DecodeError::InvalidLength);
7750 }
7751
7752 let padding = ct_padding_len(input);
7753 let mut invalid_byte = 0u8;
7754 let mut invalid_padding = 0u8;
7755 let mut read = 0;
7756
7757 while read + 4 < input.len() {
7758 let [b0, b1, b2, b3] =
7759 read_quad_or_mark_invalid(input, read, &mut invalid_byte, &mut invalid_padding);
7760 let (_, valid0) = ct_decode_alphabet_byte::<A>(b0);
7761 let (_, valid1) = ct_decode_alphabet_byte::<A>(b1);
7762 let (_, valid2) = ct_decode_alphabet_byte::<A>(b2);
7763 let (_, valid3) = ct_decode_alphabet_byte::<A>(b3);
7764
7765 invalid_byte |= !valid0;
7766 invalid_byte |= !valid1;
7767 invalid_byte |= !valid2;
7768 invalid_byte |= !valid3;
7769 invalid_padding |= ct_mask_eq_u8(b2, b'=');
7770 invalid_padding |= ct_mask_eq_u8(b3, b'=');
7771 read += 4;
7772 }
7773
7774 let final_chunk =
7775 read_quad_or_mark_invalid(input, read, &mut invalid_byte, &mut invalid_padding);
7776 let (_, final_invalid_byte, final_invalid_padding, _) =
7777 ct_padded_final_quantum::<A>(final_chunk, padding);
7778 invalid_byte |= final_invalid_byte;
7779 invalid_padding |= final_invalid_padding;
7780
7781 report_ct_error(invalid_byte, invalid_padding)
7782}
7783
7784fn ct_validate_unpadded<A: Alphabet>(input: &[u8]) -> Result<(), DecodeError> {
7785 if input.len() % 4 == 1 {
7786 return Err(DecodeError::InvalidLength);
7787 }
7788
7789 let mut invalid_byte = 0u8;
7790 let mut invalid_padding = 0u8;
7791 let mut read = 0;
7792
7793 while read + 4 <= input.len() {
7794 let [b0, b1, b2, b3] =
7795 read_quad_or_mark_invalid(input, read, &mut invalid_byte, &mut invalid_padding);
7796 let (_, valid0) = ct_decode_alphabet_byte::<A>(b0);
7797 let (_, valid1) = ct_decode_alphabet_byte::<A>(b1);
7798 let (_, valid2) = ct_decode_alphabet_byte::<A>(b2);
7799 let (_, valid3) = ct_decode_alphabet_byte::<A>(b3);
7800
7801 invalid_byte |= !valid0;
7802 invalid_byte |= !valid1;
7803 invalid_byte |= !valid2;
7804 invalid_byte |= !valid3;
7805 invalid_padding |= ct_mask_eq_u8(b0, b'=');
7806 invalid_padding |= ct_mask_eq_u8(b1, b'=');
7807 invalid_padding |= ct_mask_eq_u8(b2, b'=');
7808 invalid_padding |= ct_mask_eq_u8(b3, b'=');
7809
7810 read += 4;
7811 }
7812
7813 match read_tail_or_mark_invalid(input, read, &mut invalid_byte, &mut invalid_padding) {
7814 [] => {}
7815 [b0, b1] => {
7816 let (_, valid0) = ct_decode_alphabet_byte::<A>(*b0);
7817 let (v1, valid1) = ct_decode_alphabet_byte::<A>(*b1);
7818 invalid_byte |= !valid0;
7819 invalid_byte |= !valid1;
7820 invalid_padding |= ct_mask_eq_u8(*b0, b'=');
7821 invalid_padding |= ct_mask_eq_u8(*b1, b'=');
7822 invalid_padding |= ct_mask_nonzero_u8(v1 & 0b0000_1111);
7823 }
7824 [b0, b1, b2] => {
7825 let (_, valid0) = ct_decode_alphabet_byte::<A>(*b0);
7826 let (_, valid1) = ct_decode_alphabet_byte::<A>(*b1);
7827 let (v2, valid2) = ct_decode_alphabet_byte::<A>(*b2);
7828 invalid_byte |= !valid0;
7829 invalid_byte |= !valid1;
7830 invalid_byte |= !valid2;
7831 invalid_padding |= ct_mask_eq_u8(*b0, b'=');
7832 invalid_padding |= ct_mask_eq_u8(*b1, b'=');
7833 invalid_padding |= ct_mask_eq_u8(*b2, b'=');
7834 invalid_padding |= ct_mask_nonzero_u8(v2 & 0b0000_0011);
7835 }
7836 _ => {
7837 invalid_byte = 0xff;
7838 invalid_padding = 0xff;
7839 }
7840 }
7841
7842 report_ct_error(invalid_byte, invalid_padding)
7843}
7844
7845fn ct_padded_final_quantum<A: Alphabet>(
7846 input: [u8; 4],
7847 padding: usize,
7848) -> ([u8; 3], u8, u8, usize) {
7849 let [b0, b1, b2, b3] = input;
7850 let (v0, valid0) = ct_decode_alphabet_byte::<A>(b0);
7851 let (v1, valid1) = ct_decode_alphabet_byte::<A>(b1);
7852 let (v2, valid2) = ct_decode_alphabet_byte::<A>(b2);
7853 let (v3, valid3) = ct_decode_alphabet_byte::<A>(b3);
7854
7855 let padding_byte = match padding {
7856 0 => 0,
7857 1 => 1,
7858 2 => 2,
7859 _ => return ([0; 3], 0xff, 0xff, 0),
7860 };
7861 let no_padding = ct_mask_eq_u8(padding_byte, 0);
7862 let one_padding = ct_mask_eq_u8(padding_byte, 1);
7863 let two_padding = ct_mask_eq_u8(padding_byte, 2);
7864 let require_v2 = no_padding | one_padding;
7865 let require_v3 = no_padding;
7866
7867 let invalid_byte = !valid0 | !valid1 | (!valid2 & require_v2) | (!valid3 & require_v3);
7868 let invalid_padding = (ct_mask_nonzero_u8(v1 & 0b0000_1111) & two_padding)
7869 | ((ct_mask_eq_u8(b2, b'=') | ct_mask_nonzero_u8(v2 & 0b0000_0011)) & one_padding)
7870 | ((ct_mask_eq_u8(b2, b'=') | ct_mask_eq_u8(b3, b'=')) & no_padding);
7871
7872 (
7873 [(v0 << 2) | (v1 >> 4), (v1 << 4) | (v2 >> 2), (v2 << 6) | v3],
7874 invalid_byte,
7875 invalid_padding,
7876 3 - padding,
7877 )
7878}
7879
7880fn ct_decode_padded<A: Alphabet>(input: &[u8], output: &mut [u8]) -> Result<usize, DecodeError> {
7881 if !input.len().is_multiple_of(4) {
7882 return Err(DecodeError::InvalidLength);
7883 }
7884
7885 let padding = ct_padding_len(input);
7886 let required = input.len() / 4 * 3 - padding;
7887 if output.len() < required {
7888 return Err(DecodeError::OutputTooSmall {
7889 required,
7890 available: output.len(),
7891 });
7892 }
7893
7894 let mut invalid_byte = 0u8;
7895 let mut invalid_padding = 0u8;
7896 let mut write = 0;
7897 let mut read = 0;
7898
7899 while read + 4 < input.len() {
7900 let [b0, b1, b2, b3] =
7901 read_quad_or_mark_invalid(input, read, &mut invalid_byte, &mut invalid_padding);
7902 let (v0, valid0) = ct_decode_alphabet_byte::<A>(b0);
7903 let (v1, valid1) = ct_decode_alphabet_byte::<A>(b1);
7904 let (v2, valid2) = ct_decode_alphabet_byte::<A>(b2);
7905 let (v3, valid3) = ct_decode_alphabet_byte::<A>(b3);
7906
7907 invalid_byte |= !valid0;
7908 invalid_byte |= !valid1;
7909 invalid_byte |= !valid2;
7910 invalid_byte |= !valid3;
7911 invalid_padding |= ct_mask_eq_u8(b2, b'=');
7912 invalid_padding |= ct_mask_eq_u8(b3, b'=');
7913 output[write] = (v0 << 2) | (v1 >> 4);
7914 output[write + 1] = (v1 << 4) | (v2 >> 2);
7915 output[write + 2] = (v2 << 6) | v3;
7916 write += 3;
7917 read += 4;
7918 }
7919
7920 let final_chunk =
7921 read_quad_or_mark_invalid(input, read, &mut invalid_byte, &mut invalid_padding);
7922 let (final_bytes, final_invalid_byte, final_invalid_padding, final_written) =
7923 ct_padded_final_quantum::<A>(final_chunk, padding);
7924 invalid_byte |= final_invalid_byte;
7925 invalid_padding |= final_invalid_padding;
7926 output[write..write + final_written].copy_from_slice(&final_bytes[..final_written]);
7927 write += final_written;
7928
7929 report_ct_error(invalid_byte, invalid_padding)?;
7930 Ok(write)
7931}
7932
7933fn ct_decode_padded_in_place<A: Alphabet>(buffer: &mut [u8]) -> Result<usize, DecodeError> {
7934 if !buffer.len().is_multiple_of(4) {
7935 return Err(DecodeError::InvalidLength);
7936 }
7937
7938 let padding = ct_padding_len(buffer);
7939 let required = buffer.len() / 4 * 3 - padding;
7940 if required > buffer.len() {
7941 wipe_bytes(buffer);
7942 return Err(DecodeError::InvalidInput);
7943 }
7944
7945 let mut invalid_byte = 0u8;
7946 let mut invalid_padding = 0u8;
7947 let mut write = 0;
7948 let mut read = 0;
7949
7950 while read + 4 < buffer.len() {
7951 let [b0, b1, b2, b3] =
7952 read_quad_or_mark_invalid(buffer, read, &mut invalid_byte, &mut invalid_padding);
7953 let (v0, valid0) = ct_decode_alphabet_byte::<A>(b0);
7954 let (v1, valid1) = ct_decode_alphabet_byte::<A>(b1);
7955 let (v2, valid2) = ct_decode_alphabet_byte::<A>(b2);
7956 let (v3, valid3) = ct_decode_alphabet_byte::<A>(b3);
7957
7958 invalid_byte |= !valid0;
7959 invalid_byte |= !valid1;
7960 invalid_byte |= !valid2;
7961 invalid_byte |= !valid3;
7962 invalid_padding |= ct_mask_eq_u8(b2, b'=');
7963 invalid_padding |= ct_mask_eq_u8(b3, b'=');
7964 buffer[write] = (v0 << 2) | (v1 >> 4);
7965 buffer[write + 1] = (v1 << 4) | (v2 >> 2);
7966 buffer[write + 2] = (v2 << 6) | v3;
7967 write += 3;
7968 read += 4;
7969 }
7970
7971 let final_chunk =
7972 read_quad_or_mark_invalid(buffer, read, &mut invalid_byte, &mut invalid_padding);
7973 let (final_bytes, final_invalid_byte, final_invalid_padding, final_written) =
7974 ct_padded_final_quantum::<A>(final_chunk, padding);
7975 invalid_byte |= final_invalid_byte;
7976 invalid_padding |= final_invalid_padding;
7977 buffer[write..write + final_written].copy_from_slice(&final_bytes[..final_written]);
7978 write += final_written;
7979
7980 if write != required {
7981 ct_error_gate_barrier(invalid_byte, invalid_padding);
7982 wipe_bytes(buffer);
7983 return Err(DecodeError::InvalidInput);
7984 }
7985 if let Err(err) = report_ct_error(invalid_byte, invalid_padding) {
7986 wipe_bytes(buffer);
7987 return Err(err);
7988 }
7989 Ok(write)
7990}
7991
7992fn ct_decode_unpadded<A: Alphabet>(input: &[u8], output: &mut [u8]) -> Result<usize, DecodeError> {
7993 if input.len() % 4 == 1 {
7994 return Err(DecodeError::InvalidLength);
7995 }
7996
7997 let required = decoded_capacity(input.len());
7998 if output.len() < required {
7999 return Err(DecodeError::OutputTooSmall {
8000 required,
8001 available: output.len(),
8002 });
8003 }
8004
8005 let mut invalid_byte = 0u8;
8006 let mut invalid_padding = 0u8;
8007 let mut write = 0;
8008 let mut read = 0;
8009
8010 while read + 4 <= input.len() {
8011 let [b0, b1, b2, b3] =
8012 read_quad_or_mark_invalid(input, read, &mut invalid_byte, &mut invalid_padding);
8013 let (v0, valid0) = ct_decode_alphabet_byte::<A>(b0);
8014 let (v1, valid1) = ct_decode_alphabet_byte::<A>(b1);
8015 let (v2, valid2) = ct_decode_alphabet_byte::<A>(b2);
8016 let (v3, valid3) = ct_decode_alphabet_byte::<A>(b3);
8017
8018 invalid_byte |= !valid0;
8019 invalid_byte |= !valid1;
8020 invalid_byte |= !valid2;
8021 invalid_byte |= !valid3;
8022 invalid_padding |= ct_mask_eq_u8(b0, b'=');
8023 invalid_padding |= ct_mask_eq_u8(b1, b'=');
8024 invalid_padding |= ct_mask_eq_u8(b2, b'=');
8025 invalid_padding |= ct_mask_eq_u8(b3, b'=');
8026
8027 output[write] = (v0 << 2) | (v1 >> 4);
8028 output[write + 1] = (v1 << 4) | (v2 >> 2);
8029 output[write + 2] = (v2 << 6) | v3;
8030 read += 4;
8031 write += 3;
8032 }
8033
8034 match read_tail_or_mark_invalid(input, read, &mut invalid_byte, &mut invalid_padding) {
8035 [] => {}
8036 [b0, b1] => {
8037 let (v0, valid0) = ct_decode_alphabet_byte::<A>(*b0);
8038 let (v1, valid1) = ct_decode_alphabet_byte::<A>(*b1);
8039 invalid_byte |= !valid0;
8040 invalid_byte |= !valid1;
8041 invalid_padding |= ct_mask_eq_u8(*b0, b'=');
8042 invalid_padding |= ct_mask_eq_u8(*b1, b'=');
8043 invalid_padding |= ct_mask_nonzero_u8(v1 & 0b0000_1111);
8044 output[write] = (v0 << 2) | (v1 >> 4);
8045 write += 1;
8046 }
8047 [b0, b1, b2] => {
8048 let (v0, valid0) = ct_decode_alphabet_byte::<A>(*b0);
8049 let (v1, valid1) = ct_decode_alphabet_byte::<A>(*b1);
8050 let (v2, valid2) = ct_decode_alphabet_byte::<A>(*b2);
8051 invalid_byte |= !valid0;
8052 invalid_byte |= !valid1;
8053 invalid_byte |= !valid2;
8054 invalid_padding |= ct_mask_eq_u8(*b0, b'=');
8055 invalid_padding |= ct_mask_eq_u8(*b1, b'=');
8056 invalid_padding |= ct_mask_eq_u8(*b2, b'=');
8057 invalid_padding |= ct_mask_nonzero_u8(v2 & 0b0000_0011);
8058 output[write] = (v0 << 2) | (v1 >> 4);
8059 output[write + 1] = (v1 << 4) | (v2 >> 2);
8060 write += 2;
8061 }
8062 _ => {
8063 invalid_byte = 0xff;
8064 invalid_padding = 0xff;
8065 }
8066 }
8067
8068 report_ct_error(invalid_byte, invalid_padding)?;
8069 Ok(write)
8070}
8071
8072fn ct_decode_unpadded_in_place<A: Alphabet>(buffer: &mut [u8]) -> Result<usize, DecodeError> {
8073 if buffer.len() % 4 == 1 {
8074 return Err(DecodeError::InvalidLength);
8075 }
8076
8077 let required = decoded_capacity(buffer.len());
8078 if required > buffer.len() {
8079 wipe_bytes(buffer);
8080 return Err(DecodeError::InvalidInput);
8081 }
8082
8083 let mut invalid_byte = 0u8;
8084 let mut invalid_padding = 0u8;
8085 let mut write = 0;
8086 let mut read = 0;
8087
8088 while read + 4 <= buffer.len() {
8089 let [b0, b1, b2, b3] =
8090 read_quad_or_mark_invalid(buffer, read, &mut invalid_byte, &mut invalid_padding);
8091 let (v0, valid0) = ct_decode_alphabet_byte::<A>(b0);
8092 let (v1, valid1) = ct_decode_alphabet_byte::<A>(b1);
8093 let (v2, valid2) = ct_decode_alphabet_byte::<A>(b2);
8094 let (v3, valid3) = ct_decode_alphabet_byte::<A>(b3);
8095
8096 invalid_byte |= !valid0;
8097 invalid_byte |= !valid1;
8098 invalid_byte |= !valid2;
8099 invalid_byte |= !valid3;
8100 invalid_padding |= ct_mask_eq_u8(b0, b'=');
8101 invalid_padding |= ct_mask_eq_u8(b1, b'=');
8102 invalid_padding |= ct_mask_eq_u8(b2, b'=');
8103 invalid_padding |= ct_mask_eq_u8(b3, b'=');
8104
8105 buffer[write] = (v0 << 2) | (v1 >> 4);
8106 buffer[write + 1] = (v1 << 4) | (v2 >> 2);
8107 buffer[write + 2] = (v2 << 6) | v3;
8108 read += 4;
8109 write += 3;
8110 }
8111
8112 let tail = read_tail_or_mark_invalid(buffer, read, &mut invalid_byte, &mut invalid_padding);
8113 match tail {
8114 [] => {}
8115 [b0, b1] => {
8116 let (v0, valid0) = ct_decode_alphabet_byte::<A>(*b0);
8117 let (v1, valid1) = ct_decode_alphabet_byte::<A>(*b1);
8118 invalid_byte |= !valid0;
8119 invalid_byte |= !valid1;
8120 invalid_padding |= ct_mask_eq_u8(*b0, b'=');
8121 invalid_padding |= ct_mask_eq_u8(*b1, b'=');
8122 invalid_padding |= ct_mask_nonzero_u8(v1 & 0b0000_1111);
8123 buffer[write] = (v0 << 2) | (v1 >> 4);
8124 write += 1;
8125 }
8126 [b0, b1, b2] => {
8127 let (v0, valid0) = ct_decode_alphabet_byte::<A>(*b0);
8128 let (v1, valid1) = ct_decode_alphabet_byte::<A>(*b1);
8129 let (v2, valid2) = ct_decode_alphabet_byte::<A>(*b2);
8130 invalid_byte |= !valid0;
8131 invalid_byte |= !valid1;
8132 invalid_byte |= !valid2;
8133 invalid_padding |= ct_mask_eq_u8(*b0, b'=');
8134 invalid_padding |= ct_mask_eq_u8(*b1, b'=');
8135 invalid_padding |= ct_mask_eq_u8(*b2, b'=');
8136 invalid_padding |= ct_mask_nonzero_u8(v2 & 0b0000_0011);
8137 buffer[write] = (v0 << 2) | (v1 >> 4);
8138 buffer[write + 1] = (v1 << 4) | (v2 >> 2);
8139 write += 2;
8140 }
8141 _ => {
8142 invalid_byte = 0xff;
8143 invalid_padding = 0xff;
8144 }
8145 }
8146
8147 if write != required {
8148 ct_error_gate_barrier(invalid_byte, invalid_padding);
8149 wipe_bytes(buffer);
8150 return Err(DecodeError::InvalidInput);
8151 }
8152 if let Err(err) = report_ct_error(invalid_byte, invalid_padding) {
8153 wipe_bytes(buffer);
8154 return Err(err);
8155 }
8156 Ok(write)
8157}
8158
8159fn read_tail(input: &[u8], offset: usize) -> Result<&[u8], DecodeError> {
8160 input.get(offset..).ok_or(DecodeError::InvalidLength)
8161}
8162
8163fn read_quad_or_mark_invalid(
8164 input: &[u8],
8165 offset: usize,
8166 invalid_byte: &mut u8,
8167 invalid_padding: &mut u8,
8168) -> [u8; 4] {
8169 if let Ok(quad) = read_quad(input, offset) {
8170 quad
8171 } else {
8172 debug_assert!(
8173 false,
8174 "read_quad failed inside length-validated constant-time decode loop"
8175 );
8176 *invalid_byte = 0xff;
8177 *invalid_padding = 0xff;
8178 [0; 4]
8179 }
8180}
8181
8182fn read_tail_or_mark_invalid<'a>(
8183 input: &'a [u8],
8184 offset: usize,
8185 invalid_byte: &mut u8,
8186 invalid_padding: &mut u8,
8187) -> &'a [u8] {
8188 if let Ok(tail) = read_tail(input, offset) {
8189 tail
8190 } else {
8191 debug_assert!(
8192 false,
8193 "read_tail failed inside length-validated constant-time decode loop"
8194 );
8195 *invalid_byte = 0xff;
8196 *invalid_padding = 0xff;
8197 &[]
8198 }
8199}
8200
8201#[inline(never)]
8202#[allow(unsafe_code)]
8203fn ct_decode_alphabet_byte<A: Alphabet>(byte: u8) -> (u8, u8) {
8204 let mut decoded = 0u8;
8205 let mut valid = 0u8;
8206 let mut candidate = 0u8;
8207
8208 while candidate < 64 {
8209 let matches = core::hint::black_box(ct_mask_eq_u8(
8210 core::hint::black_box(byte),
8211 core::hint::black_box(A::ENCODE[candidate as usize]),
8212 ));
8213 decoded = core::hint::black_box(
8214 core::hint::black_box(decoded) | core::hint::black_box(candidate & matches),
8215 );
8216 decoded = unsafe { core::ptr::read_volatile(&raw const decoded) };
8220 valid =
8221 core::hint::black_box(core::hint::black_box(valid) | core::hint::black_box(matches));
8222 valid = unsafe { core::ptr::read_volatile(&raw const valid) };
8226 candidate += 1;
8227 }
8228
8229 (decoded, valid)
8230}
8231
8232fn ct_padding_len(input: &[u8]) -> usize {
8233 let Some((&last, before_last_prefix)) = input.split_last() else {
8234 return 0;
8235 };
8236 let Some(&before_last) = before_last_prefix.last() else {
8237 return 0;
8238 };
8239 usize::from(ct_mask_eq_u8(last, b'=') & 1) + usize::from(ct_mask_eq_u8(before_last, b'=') & 1)
8240}
8241
8242fn report_ct_error(invalid_byte: u8, invalid_padding: u8) -> Result<(), DecodeError> {
8243 ct_error_gate_barrier(invalid_byte, invalid_padding);
8244
8245 if (invalid_byte | invalid_padding) != 0 {
8246 Err(DecodeError::InvalidInput)
8247 } else {
8248 Ok(())
8249 }
8250}
8251
8252#[cfg(kani)]
8253mod kani_proofs {
8254 use super::{
8255 STANDARD, Standard, checked_encoded_len, ct, decode_byte, decode_chunk,
8256 decode_tail_unpadded, decoded_capacity, validate_tail_unpadded,
8257 };
8258
8259 #[kani::proof]
8260 fn checked_encoded_len_is_bounded_for_small_inputs() {
8261 let len = usize::from(kani::any::<u8>());
8262 let padded = kani::any::<bool>();
8263 let encoded = checked_encoded_len(len, padded).expect("u8 input length cannot overflow");
8264
8265 assert!(encoded >= len);
8266 assert!(encoded <= len / 3 * 4 + 4);
8267 }
8268
8269 #[kani::proof]
8270 fn decoded_capacity_is_bounded_for_small_inputs() {
8271 let len = usize::from(kani::any::<u8>());
8272 let capacity = decoded_capacity(len);
8273
8274 assert!(capacity <= len / 4 * 3 + 2);
8275 }
8276
8277 #[kani::proof]
8278 #[kani::unwind(3)]
8279 fn standard_in_place_decode_returns_prefix_within_buffer() {
8280 let mut buffer = kani::any::<[u8; 8]>();
8281 let result = STANDARD.decode_in_place(&mut buffer);
8282
8283 if let Ok(decoded) = result {
8284 assert!(decoded.len() <= 8);
8285 }
8286 }
8287
8288 #[kani::proof]
8289 #[kani::unwind(3)]
8290 fn standard_decode_slice_returns_written_within_output() {
8291 let input = kani::any::<[u8; 4]>();
8292 let mut output = kani::any::<[u8; 3]>();
8293 let result = STANDARD.decode_slice(&input, &mut output);
8294
8295 if let Ok(written) = result {
8296 assert!(written <= output.len());
8297 }
8298 }
8299
8300 #[kani::proof]
8301 #[kani::unwind(3)]
8302 fn standard_decode_chunk_returns_written_within_output() {
8303 let input = kani::any::<[u8; 4]>();
8304 let mut output = kani::any::<[u8; 3]>();
8305 let result = decode_chunk::<Standard, true>(input, &mut output);
8306
8307 if let Ok(written) = result {
8308 assert!(written <= output.len());
8309 assert!(written <= 3);
8310 }
8311 }
8312
8313 #[kani::proof]
8314 #[kani::unwind(3)]
8315 fn standard_decode_chunk_bit_packing_matches_decoded_values() {
8316 let input = kani::any::<[u8; 4]>();
8317 let mut output = kani::any::<[u8; 3]>();
8318 let result = decode_chunk::<Standard, true>(input, &mut output);
8319
8320 if let Ok(written) = result {
8321 let v0 = decode_byte::<Standard>(input[0], 0).expect("successful chunk has v0");
8322 let v1 = decode_byte::<Standard>(input[1], 1).expect("successful chunk has v1");
8323
8324 assert!(output[0] == ((v0 << 2) | (v1 >> 4)));
8325
8326 if written >= 2 {
8327 let v2 = decode_byte::<Standard>(input[2], 2).expect("successful chunk has v2");
8328 assert!(output[1] == ((v1 << 4) | (v2 >> 2)));
8329 }
8330
8331 if written == 3 {
8332 let v2 = decode_byte::<Standard>(input[2], 2).expect("successful chunk has v2");
8333 let v3 = decode_byte::<Standard>(input[3], 3).expect("successful chunk has v3");
8334 assert!(output[2] == ((v2 << 6) | v3));
8335 }
8336 }
8337 }
8338
8339 #[kani::proof]
8340 #[kani::unwind(3)]
8341 fn standard_validate_tail_unpadded_accepts_or_rejects_without_panic() {
8342 let input = kani::any::<[u8; 3]>();
8343 let len = usize::from(kani::any::<u8>() % 4);
8344 let result = validate_tail_unpadded::<Standard>(&input[..len]);
8345
8346 if result.is_ok() {
8347 assert!(len == 0 || len == 2 || len == 3);
8348 }
8349 }
8350
8351 #[kani::proof]
8352 #[kani::unwind(3)]
8353 fn standard_decode_two_byte_tail_returns_written_within_output() {
8354 let input = kani::any::<[u8; 2]>();
8355 let mut output = kani::any::<[u8; 1]>();
8356 let result = decode_tail_unpadded::<Standard>(&input, &mut output);
8357
8358 if let Ok(written) = result {
8359 assert!(written <= output.len());
8360 assert!(written == 1);
8361 }
8362 }
8363
8364 #[kani::proof]
8365 #[kani::unwind(3)]
8366 fn standard_decode_three_byte_tail_returns_written_within_output() {
8367 let input = kani::any::<[u8; 3]>();
8368 let mut output = kani::any::<[u8; 2]>();
8369 let result = decode_tail_unpadded::<Standard>(&input, &mut output);
8370
8371 if let Ok(written) = result {
8372 assert!(written <= output.len());
8373 assert!(written == 2);
8374 }
8375 }
8376
8377 #[kani::proof]
8378 #[kani::unwind(3)]
8379 fn standard_decode_slice_clear_tail_clears_output_on_error() {
8380 let input = kani::any::<[u8; 4]>();
8381 let mut output = kani::any::<[u8; 3]>();
8382 let result = STANDARD.decode_slice_clear_tail(&input, &mut output);
8383
8384 if result.is_err() {
8385 assert!(output.iter().all(|byte| *byte == 0));
8386 }
8387 }
8388
8389 #[kani::proof]
8390 #[kani::unwind(3)]
8391 fn standard_encode_slice_returns_written_within_output() {
8392 let input = kani::any::<[u8; 3]>();
8393 let mut output = kani::any::<[u8; 4]>();
8394 let result = STANDARD.encode_slice(&input, &mut output);
8395
8396 if let Ok(written) = result {
8397 assert!(written <= output.len());
8398 }
8399 }
8400
8401 #[kani::proof]
8402 #[kani::unwind(4)]
8403 fn standard_encode_in_place_returns_prefix_within_buffer() {
8404 let mut buffer = kani::any::<[u8; 8]>();
8405 let input_len = usize::from(kani::any::<u8>() % 9);
8406 let result = STANDARD.encode_in_place(&mut buffer, input_len);
8407
8408 if let Ok(encoded) = result {
8409 assert!(encoded.len() <= 8);
8410 }
8411 }
8412
8413 #[kani::proof]
8414 #[kani::unwind(3)]
8415 fn standard_clear_tail_decode_clears_buffer_on_error() {
8416 let mut buffer = kani::any::<[u8; 4]>();
8417 let result = STANDARD.decode_in_place_clear_tail(&mut buffer);
8418
8419 if result.is_err() {
8420 assert!(buffer.iter().all(|byte| *byte == 0));
8421 }
8422 }
8423
8424 #[kani::proof]
8425 #[kani::unwind(3)]
8426 fn ct_standard_decode_slice_returns_written_within_output() {
8427 let input = kani::any::<[u8; 4]>();
8428 let mut output = kani::any::<[u8; 3]>();
8429 let result = ct::STANDARD.decode_slice_clear_tail(&input, &mut output);
8430
8431 if let Ok(written) = result {
8432 assert!(written <= output.len());
8433 }
8434 }
8435
8436 #[kani::proof]
8437 #[kani::unwind(3)]
8438 fn ct_standard_decode_slice_clear_tail_clears_output_on_error() {
8439 let input = kani::any::<[u8; 4]>();
8440 let mut output = kani::any::<[u8; 3]>();
8441 let result = ct::STANDARD.decode_slice_clear_tail(&input, &mut output);
8442
8443 if result.is_err() {
8444 assert!(output.iter().all(|byte| *byte == 0));
8445 }
8446 }
8447
8448 #[kani::proof]
8449 #[kani::unwind(3)]
8450 fn ct_standard_decode_in_place_clear_tail_clears_buffer_on_error() {
8451 let mut buffer = kani::any::<[u8; 4]>();
8452 let result = ct::STANDARD.decode_in_place_clear_tail(&mut buffer);
8453
8454 if result.is_err() {
8455 assert!(buffer.iter().all(|byte| *byte == 0));
8456 }
8457 }
8458
8459 #[kani::proof]
8460 #[kani::unwind(3)]
8461 fn ct_standard_validate_matches_decode_for_one_quantum() {
8462 let input = kani::any::<[u8; 4]>();
8463 let mut output = kani::any::<[u8; 3]>();
8464
8465 let validate_ok = ct::STANDARD.validate_result(&input).is_ok();
8466 let decode_ok = ct::STANDARD
8467 .decode_slice_clear_tail(&input, &mut output)
8468 .is_ok();
8469
8470 assert!(validate_ok == decode_ok);
8471 }
8472}
8473
8474#[cfg(test)]
8475mod tests {
8476 use super::*;
8477
8478 fn fill_pattern(output: &mut [u8], seed: usize) {
8479 for (index, byte) in output.iter_mut().enumerate() {
8480 let value = (index * 73 + seed * 19) % 256;
8481 *byte = u8::try_from(value).unwrap();
8482 }
8483 }
8484
8485 fn assert_encode_backend_matches_scalar<A, const PAD: bool>(input: &[u8])
8486 where
8487 A: Alphabet,
8488 {
8489 let engine = Engine::<A, PAD>::new();
8490 let mut dispatched = [0x55; 256];
8491 let mut scalar = [0xaa; 256];
8492
8493 let dispatched_result = engine.encode_slice(input, &mut dispatched);
8494 let scalar_result = backend::scalar_reference_encode_slice::<A, PAD>(input, &mut scalar);
8495
8496 assert_eq!(dispatched_result, scalar_result);
8497 if let Ok(written) = dispatched_result {
8498 assert_eq!(&dispatched[..written], &scalar[..written]);
8499 }
8500
8501 let required = checked_encoded_len(input.len(), PAD).unwrap();
8502 if required > 0 {
8503 let mut dispatched_short = [0x55; 256];
8504 let mut scalar_short = [0xaa; 256];
8505 let available = required - 1;
8506
8507 assert_eq!(
8508 engine.encode_slice(input, &mut dispatched_short[..available]),
8509 backend::scalar_reference_encode_slice::<A, PAD>(
8510 input,
8511 &mut scalar_short[..available],
8512 )
8513 );
8514 }
8515 }
8516
8517 fn assert_decode_backend_matches_scalar<A, const PAD: bool>(input: &[u8])
8518 where
8519 A: Alphabet,
8520 {
8521 let engine = Engine::<A, PAD>::new();
8522 let mut dispatched = [0x55; 128];
8523 let mut scalar = [0xaa; 128];
8524
8525 let dispatched_result = engine.decode_slice(input, &mut dispatched);
8526 let scalar_result = backend::scalar_reference_decode_slice::<A, PAD>(input, &mut scalar);
8527
8528 assert_eq!(dispatched_result, scalar_result);
8529 if let Ok(written) = dispatched_result {
8530 assert_eq!(&dispatched[..written], &scalar[..written]);
8531
8532 if written > 0 {
8533 let mut dispatched_short = [0x55; 128];
8534 let mut scalar_short = [0xaa; 128];
8535 let available = written - 1;
8536
8537 assert_eq!(
8538 engine.decode_slice(input, &mut dispatched_short[..available]),
8539 backend::scalar_reference_decode_slice::<A, PAD>(
8540 input,
8541 &mut scalar_short[..available],
8542 )
8543 );
8544 }
8545 }
8546 }
8547
8548 fn assert_backend_round_trip_matches_scalar<A, const PAD: bool>(input: &[u8])
8549 where
8550 A: Alphabet,
8551 {
8552 assert_encode_backend_matches_scalar::<A, PAD>(input);
8553
8554 let mut encoded = [0; 256];
8555 let encoded_len =
8556 backend::scalar_reference_encode_slice::<A, PAD>(input, &mut encoded).unwrap();
8557 assert_decode_backend_matches_scalar::<A, PAD>(&encoded[..encoded_len]);
8558 }
8559
8560 fn assert_standard_decode_chunk_matches_input(input: &[u8]) {
8561 let mut encoded = [0u8; 4];
8562 let encoded_len = STANDARD.encode_slice(input, &mut encoded).unwrap();
8563 assert_eq!(encoded_len, 4);
8564
8565 let chunk = [encoded[0], encoded[1], encoded[2], encoded[3]];
8566 let mut decoded = [0u8; 3];
8567 let decoded_len = decode_chunk::<Standard, true>(chunk, &mut decoded).unwrap();
8568
8569 assert_eq!(decoded_len, input.len());
8570 assert_eq!(&decoded[..decoded_len], input);
8571 }
8572
8573 #[test]
8574 fn backend_dispatch_matches_scalar_reference_for_canonical_inputs() {
8575 let mut input = [0; 128];
8576
8577 for input_len in 0..=input.len() {
8578 fill_pattern(&mut input[..input_len], input_len);
8579 let input = &input[..input_len];
8580
8581 assert_backend_round_trip_matches_scalar::<Standard, true>(input);
8582 assert_backend_round_trip_matches_scalar::<Standard, false>(input);
8583 assert_backend_round_trip_matches_scalar::<UrlSafe, true>(input);
8584 assert_backend_round_trip_matches_scalar::<UrlSafe, false>(input);
8585 }
8586 }
8587
8588 #[test]
8589 fn backend_dispatch_matches_scalar_reference_for_malformed_inputs() {
8590 for input in [
8591 &b"Z"[..],
8592 b"====",
8593 b"AA=A",
8594 b"Zh==",
8595 b"Zm9=",
8596 b"Zm9v$g==",
8597 b"Zm9vZh==",
8598 ] {
8599 assert_decode_backend_matches_scalar::<Standard, true>(input);
8600 }
8601
8602 for input in [&b"Z"[..], b"AA=A", b"Zh", b"Zm9", b"Zm9vYg$"] {
8603 assert_decode_backend_matches_scalar::<Standard, false>(input);
8604 }
8605
8606 assert_decode_backend_matches_scalar::<UrlSafe, true>(b"AA+A");
8607 assert_decode_backend_matches_scalar::<UrlSafe, false>(b"AA/A");
8608 assert_decode_backend_matches_scalar::<Standard, true>(b"AA-A");
8609 assert_decode_backend_matches_scalar::<Standard, false>(b"AA_A");
8610 }
8611
8612 #[test]
8613 fn decode_chunk_bit_packing_matches_exhaustive_small_inputs() {
8614 for byte in u8::MIN..=u8::MAX {
8615 assert_standard_decode_chunk_matches_input(&[byte]);
8616 }
8617
8618 for first in u8::MIN..=u8::MAX {
8619 for second in u8::MIN..=u8::MAX {
8620 assert_standard_decode_chunk_matches_input(&[first, second]);
8621 }
8622 }
8623 }
8624
8625 #[test]
8626 fn decode_chunk_bit_packing_matches_representative_full_quanta() {
8627 const SAMPLES: [u8; 16] = [
8628 0, 1, 2, 15, 16, 31, 32, 63, 64, 95, 127, 128, 191, 192, 254, 255,
8629 ];
8630
8631 for first in SAMPLES {
8632 for second in SAMPLES {
8633 for third in SAMPLES {
8634 assert_standard_decode_chunk_matches_input(&[first, second, third]);
8635 }
8636 }
8637 }
8638 }
8639
8640 #[test]
8641 fn ct_padded_final_quantum_fails_closed_for_invalid_padding_count() {
8642 let (_, invalid_byte, invalid_padding, written) =
8643 ct_padded_final_quantum::<Standard>(*b"ABCD", 3);
8644
8645 assert_ne!(invalid_byte, 0);
8646 assert_ne!(invalid_padding, 0);
8647 assert_eq!(written, 0);
8648 assert_eq!(
8649 report_ct_error(invalid_byte, invalid_padding),
8650 Err(DecodeError::InvalidInput)
8651 );
8652 }
8653
8654 #[cfg(feature = "simd")]
8655 #[test]
8656 fn simd_dispatch_scaffold_keeps_scalar_active() {
8657 assert_eq!(simd::active_backend(), simd::ActiveBackend::Scalar);
8658 let _candidate = simd::detected_candidate();
8659 }
8660
8661 #[test]
8662 fn encodes_standard_vectors() {
8663 let vectors = [
8664 (&b""[..], &b""[..]),
8665 (&b"f"[..], &b"Zg=="[..]),
8666 (&b"fo"[..], &b"Zm8="[..]),
8667 (&b"foo"[..], &b"Zm9v"[..]),
8668 (&b"foob"[..], &b"Zm9vYg=="[..]),
8669 (&b"fooba"[..], &b"Zm9vYmE="[..]),
8670 (&b"foobar"[..], &b"Zm9vYmFy"[..]),
8671 ];
8672 for (input, expected) in vectors {
8673 let mut output = [0u8; 16];
8674 let written = STANDARD.encode_slice(input, &mut output).unwrap();
8675 assert_eq!(&output[..written], expected);
8676 }
8677 }
8678
8679 #[test]
8680 fn decodes_standard_vectors() {
8681 let vectors = [
8682 (&b""[..], &b""[..]),
8683 (&b"Zg=="[..], &b"f"[..]),
8684 (&b"Zm8="[..], &b"fo"[..]),
8685 (&b"Zm9v"[..], &b"foo"[..]),
8686 (&b"Zm9vYg=="[..], &b"foob"[..]),
8687 (&b"Zm9vYmE="[..], &b"fooba"[..]),
8688 (&b"Zm9vYmFy"[..], &b"foobar"[..]),
8689 ];
8690 for (input, expected) in vectors {
8691 let mut output = [0u8; 16];
8692 let written = STANDARD.decode_slice(input, &mut output).unwrap();
8693 assert_eq!(&output[..written], expected);
8694 }
8695 }
8696
8697 #[test]
8698 fn supports_unpadded_url_safe() {
8699 let mut encoded = [0u8; 16];
8700 let written = URL_SAFE_NO_PAD
8701 .encode_slice(b"\xfb\xff", &mut encoded)
8702 .unwrap();
8703 assert_eq!(&encoded[..written], b"-_8");
8704
8705 let mut decoded = [0u8; 2];
8706 let written = URL_SAFE_NO_PAD
8707 .decode_slice(&encoded[..written], &mut decoded)
8708 .unwrap();
8709 assert_eq!(&decoded[..written], b"\xfb\xff");
8710 }
8711
8712 #[test]
8713 fn decodes_in_place() {
8714 let mut buffer = *b"Zm9vYmFy";
8715 let decoded = STANDARD_NO_PAD.decode_in_place(&mut buffer).unwrap();
8716 assert_eq!(decoded, b"foobar");
8717 }
8718
8719 #[test]
8720 fn rejects_non_canonical_padding_bits() {
8721 let mut output = [0u8; 4];
8722 assert_eq!(
8723 STANDARD.decode_slice(b"Zh==", &mut output),
8724 Err(DecodeError::InvalidPadding { index: 1 })
8725 );
8726 assert_eq!(
8727 STANDARD.decode_slice(b"Zm9=", &mut output),
8728 Err(DecodeError::InvalidPadding { index: 2 })
8729 );
8730 }
8731}