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,
349 }
350
351 impl BackendPolicy {
352 #[must_use]
361 pub const fn as_str(self) -> &'static str {
362 match self {
363 Self::ScalarExecutionOnly => "scalar-execution-only",
364 Self::SimdFeatureDisabled => "simd-feature-disabled",
365 Self::NoDetectedSimdCandidate => "no-detected-simd-candidate",
366 Self::HighAssuranceScalarOnly => "high-assurance-scalar-only",
367 }
368 }
369 }
370
371 impl core::fmt::Display for BackendPolicy {
372 fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
373 formatter.write_str(self.as_str())
374 }
375 }
376
377 #[derive(Clone, Copy, Debug, Eq, PartialEq)]
379 pub struct BackendPolicyError {
380 pub policy: BackendPolicy,
382 pub report: BackendReport,
384 }
385
386 impl core::fmt::Display for BackendPolicyError {
387 fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
388 write!(
389 formatter,
390 "runtime backend policy `{}` was not satisfied ({})",
391 self.policy, self.report,
392 )
393 }
394 }
395
396 #[cfg(feature = "std")]
397 impl std::error::Error for BackendPolicyError {}
398
399 #[derive(Clone, Copy, Debug, Eq, PartialEq)]
401 pub struct BackendReport {
402 pub active: Backend,
404 pub candidate: Backend,
406 pub candidate_detection_mode: CandidateDetectionMode,
409 pub simd_feature_enabled: bool,
411 pub accelerated_backend_active: bool,
413 pub unsafe_boundary_enforced: bool,
420 pub security_posture: SecurityPosture,
422 pub wipe_posture: WipePosture,
424 pub ct_gate_posture: CtGatePosture,
426 }
427
428 #[derive(Clone, Copy, Debug, Eq, PartialEq)]
430 pub struct BackendSnapshot {
431 pub active: &'static str,
433 pub candidate: &'static str,
435 pub candidate_detection_mode: &'static str,
437 pub candidate_required_cpu_features: &'static [&'static str],
439 pub simd_feature_enabled: bool,
441 pub accelerated_backend_active: bool,
443 pub unsafe_boundary_enforced: bool,
449 pub security_posture: &'static str,
451 pub wipe_posture: &'static str,
453 pub ct_gate_posture: &'static str,
455 }
456
457 impl core::fmt::Display for BackendReport {
458 fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
459 write!(
460 formatter,
461 "active={} candidate={} candidate_detection_mode={} candidate_required_cpu_features=",
462 self.active, self.candidate, self.candidate_detection_mode,
463 )?;
464 write_feature_list(formatter, self.candidate_required_cpu_features())?;
465 write!(
466 formatter,
467 " simd_feature_enabled={} accelerated_backend_active={} unsafe_boundary_enforced={} security_posture={} wipe_posture={} ct_gate_posture={}",
468 self.simd_feature_enabled,
469 self.accelerated_backend_active,
470 self.unsafe_boundary_enforced,
471 self.security_posture,
472 self.wipe_posture,
473 self.ct_gate_posture,
474 )
475 }
476 }
477
478 impl BackendReport {
479 #[must_use]
489 pub const fn satisfies(self, policy: BackendPolicy) -> bool {
490 match policy {
491 BackendPolicy::ScalarExecutionOnly => {
492 matches!(self.active, Backend::Scalar) && !self.accelerated_backend_active
493 }
494 BackendPolicy::SimdFeatureDisabled => !self.simd_feature_enabled,
495 BackendPolicy::NoDetectedSimdCandidate => matches!(self.candidate, Backend::Scalar),
496 BackendPolicy::HighAssuranceScalarOnly => {
497 matches!(self.active, Backend::Scalar)
498 && matches!(self.candidate, Backend::Scalar)
499 && !self.simd_feature_enabled
500 && !self.accelerated_backend_active
501 && self.unsafe_boundary_enforced
502 && matches!(
503 self.ct_gate_posture,
504 CtGatePosture::HardwareSpeculationBarrier
505 )
506 }
507 }
508 }
509
510 #[must_use]
521 pub const fn candidate_required_cpu_features(self) -> &'static [&'static str] {
522 self.candidate.required_cpu_features()
523 }
524
525 #[must_use]
534 pub const fn snapshot(self) -> BackendSnapshot {
535 BackendSnapshot {
536 active: self.active.as_str(),
537 candidate: self.candidate.as_str(),
538 candidate_detection_mode: self.candidate_detection_mode.as_str(),
539 candidate_required_cpu_features: self.candidate_required_cpu_features(),
540 simd_feature_enabled: self.simd_feature_enabled,
541 accelerated_backend_active: self.accelerated_backend_active,
542 unsafe_boundary_enforced: self.unsafe_boundary_enforced,
543 security_posture: self.security_posture.as_str(),
544 wipe_posture: self.wipe_posture.as_str(),
545 ct_gate_posture: self.ct_gate_posture.as_str(),
546 }
547 }
548 }
549
550 #[must_use]
559 pub fn backend_report() -> BackendReport {
560 let active = active_backend();
561 let candidate = detected_candidate();
562 let candidate_detection_mode = candidate_detection_mode();
563 let accelerated_backend_active = active != Backend::Scalar;
564 let unsafe_boundary_enforced = !cfg!(feature = "simd");
565 let security_posture = if accelerated_backend_active {
566 SecurityPosture::Accelerated
567 } else if candidate != Backend::Scalar {
568 SecurityPosture::SimdCandidateScalarActive
569 } else {
570 SecurityPosture::ScalarOnly
571 };
572
573 BackendReport {
574 active,
575 candidate,
576 candidate_detection_mode,
577 simd_feature_enabled: cfg!(feature = "simd"),
578 accelerated_backend_active,
579 unsafe_boundary_enforced,
580 security_posture,
581 wipe_posture: wipe_posture(),
582 ct_gate_posture: ct_gate_posture(),
583 }
584 }
585
586 const fn wipe_posture() -> WipePosture {
587 if cfg!(any(
588 target_arch = "aarch64",
589 target_arch = "arm",
590 target_arch = "riscv32",
591 target_arch = "riscv64",
592 target_arch = "x86",
593 target_arch = "x86_64",
594 )) {
595 WipePosture::HardwareFence
596 } else {
597 WipePosture::CompilerFenceOnly
598 }
599 }
600
601 const fn ct_gate_posture() -> CtGatePosture {
602 if cfg!(any(
603 target_arch = "aarch64",
604 target_arch = "x86",
605 target_arch = "x86_64"
606 )) {
607 CtGatePosture::HardwareSpeculationBarrier
608 } else if cfg!(any(
609 target_arch = "arm",
610 target_arch = "riscv32",
611 target_arch = "riscv64"
612 )) {
613 CtGatePosture::OrderingFence
614 } else {
615 CtGatePosture::CompilerFenceOnly
616 }
617 }
618
619 pub fn require_backend_policy(policy: BackendPolicy) -> Result<(), BackendPolicyError> {
628 let report = backend_report();
629 if report.satisfies(policy) {
630 Ok(())
631 } else {
632 Err(BackendPolicyError { policy, report })
633 }
634 }
635
636 fn write_feature_list(
637 formatter: &mut core::fmt::Formatter<'_>,
638 features: &[&str],
639 ) -> core::fmt::Result {
640 formatter.write_str("[")?;
641 let mut index = 0;
642 while index < features.len() {
643 if index != 0 {
644 formatter.write_str(",")?;
645 }
646 formatter.write_str(features[index])?;
647 index += 1;
648 }
649 formatter.write_str("]")
650 }
651
652 #[cfg(feature = "simd")]
653 fn active_backend() -> Backend {
654 match super::simd::active_backend() {
655 super::simd::ActiveBackend::Scalar => Backend::Scalar,
656 }
657 }
658
659 #[cfg(not(feature = "simd"))]
660 const fn active_backend() -> Backend {
661 Backend::Scalar
662 }
663
664 #[cfg(feature = "simd")]
665 fn detected_candidate() -> Backend {
666 match super::simd::detected_candidate() {
667 super::simd::Candidate::Scalar => Backend::Scalar,
668 #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
669 super::simd::Candidate::Avx512Vbmi => Backend::Avx512Vbmi,
670 #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
671 super::simd::Candidate::Avx2 => Backend::Avx2,
672 #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
673 super::simd::Candidate::Ssse3Sse41 => Backend::Ssse3Sse41,
674 #[cfg(any(target_arch = "aarch64", target_arch = "arm"))]
675 super::simd::Candidate::Neon => Backend::Neon,
676 #[cfg(target_arch = "wasm32")]
677 super::simd::Candidate::WasmSimd128 => Backend::WasmSimd128,
678 }
679 }
680
681 #[cfg(not(feature = "simd"))]
682 const fn detected_candidate() -> Backend {
683 Backend::Scalar
684 }
685
686 #[cfg(all(
687 feature = "simd",
688 feature = "std",
689 any(target_arch = "x86", target_arch = "x86_64")
690 ))]
691 const fn candidate_detection_mode() -> CandidateDetectionMode {
692 CandidateDetectionMode::RuntimeCpuFeatures
693 }
694
695 #[cfg(all(
696 feature = "simd",
697 not(all(feature = "std", any(target_arch = "x86", target_arch = "x86_64")))
698 ))]
699 const fn candidate_detection_mode() -> CandidateDetectionMode {
700 CandidateDetectionMode::CompileTimeTargetFeatures
701 }
702
703 #[cfg(not(feature = "simd"))]
704 const fn candidate_detection_mode() -> CandidateDetectionMode {
705 CandidateDetectionMode::SimdFeatureDisabled
706 }
707}
708
709#[cfg(feature = "stream")]
710pub mod stream {
711 use super::{Alphabet, DecodeError, EncodeError, Engine};
746 use std::io::{self, Read, Write};
747
748 struct OutputQueue<const CAP: usize> {
749 buffer: [u8; CAP],
750 start: usize,
751 len: usize,
752 }
753
754 impl<const CAP: usize> OutputQueue<CAP> {
755 const fn new() -> Self {
756 Self {
757 buffer: [0; CAP],
758 start: 0,
759 len: 0,
760 }
761 }
762
763 const fn is_empty(&self) -> bool {
764 self.len == 0
765 }
766
767 const fn len(&self) -> usize {
768 self.len
769 }
770
771 const fn capacity(&self) -> usize {
772 self.len + self.available_capacity()
773 }
774
775 fn push_slice(&mut self, input: &[u8]) -> io::Result<()> {
776 if input.len() > self.available_capacity() {
777 return Err(io::Error::other(
778 "base64 stream output queue capacity exceeded",
779 ));
780 }
781
782 let mut read = 0;
783 while read < input.len() {
784 let write = (self.start + self.len) % CAP;
785 self.buffer[write] = input[read];
786 self.len += 1;
787 read += 1;
788 }
789
790 Ok(())
791 }
792
793 fn copy_front(&self, output: &mut [u8]) -> usize {
794 let count = core::cmp::min(self.len, output.len());
795 let first = core::cmp::min(count, CAP - self.start);
796 output[..first].copy_from_slice(&self.buffer[self.start..self.start + first]);
797
798 let second = count - first;
799 if second > 0 {
800 output[first..first + second].copy_from_slice(&self.buffer[..second]);
801 }
802
803 count
804 }
805
806 fn discard_front(&mut self, count: usize) {
807 let count = core::cmp::min(count, self.len);
808 let first = core::cmp::min(count, CAP - self.start);
809 crate::wipe_bytes(&mut self.buffer[self.start..self.start + first]);
810
811 let second = count - first;
812 if second > 0 {
813 crate::wipe_bytes(&mut self.buffer[..second]);
814 }
815
816 self.start = (self.start + count) % CAP;
817 self.len -= count;
818 if self.len == 0 {
819 self.start = 0;
820 }
821 }
822
823 fn pop_slice(&mut self, output: &mut [u8]) -> usize {
824 let count = self.copy_front(output);
825 self.discard_front(count);
826 count
827 }
828
829 fn clear_all(&mut self) {
830 crate::wipe_bytes(&mut self.buffer);
831 self.start = 0;
832 self.len = 0;
833 }
834
835 const fn available_capacity(&self) -> usize {
836 CAP - self.len
837 }
838 }
839
840 pub struct Encoder<W, A, const PAD: bool>
848 where
849 A: Alphabet,
850 {
851 inner: Option<W>,
852 engine: Engine<A, PAD>,
853 pending: [u8; 2],
854 pending_len: usize,
855 output: OutputQueue<1024>,
856 finalized: bool,
857 }
858
859 impl<W, A, const PAD: bool> Encoder<W, A, PAD>
860 where
861 A: Alphabet,
862 {
863 #[must_use]
865 pub const fn new(inner: W, engine: Engine<A, PAD>) -> Self {
866 Self {
867 inner: Some(inner),
868 engine,
869 pending: [0; 2],
870 pending_len: 0,
871 output: OutputQueue::new(),
872 finalized: false,
873 }
874 }
875
876 #[must_use]
878 pub fn get_ref(&self) -> &W {
879 self.inner_ref()
880 }
881
882 pub fn get_mut(&mut self) -> &mut W {
884 self.inner_mut()
885 }
886
887 #[must_use]
889 pub const fn engine(&self) -> Engine<A, PAD> {
890 self.engine
891 }
892
893 #[must_use]
895 pub const fn is_padded(&self) -> bool {
896 PAD
897 }
898
899 #[must_use]
902 pub const fn pending_len(&self) -> usize {
903 self.pending_len
904 }
905
906 #[must_use]
909 pub const fn has_pending_input(&self) -> bool {
910 self.pending_len != 0
911 }
912
913 #[must_use]
918 pub const fn pending_input_needed_len(&self) -> usize {
919 if self.has_pending_input() {
920 3 - self.pending_len
921 } else {
922 0
923 }
924 }
925
926 #[must_use]
929 pub const fn buffered_output_len(&self) -> usize {
930 self.output.len()
931 }
932
933 #[must_use]
936 pub const fn buffered_output_capacity(&self) -> usize {
937 self.output.capacity()
938 }
939
940 #[must_use]
943 pub const fn buffered_output_remaining_capacity(&self) -> usize {
944 self.output.available_capacity()
945 }
946
947 #[must_use]
950 pub const fn has_buffered_output(&self) -> bool {
951 !self.output.is_empty()
952 }
953
954 #[must_use]
958 pub const fn is_finalized(&self) -> bool {
959 self.finalized
960 }
961
962 #[must_use]
965 pub const fn can_into_inner(&self) -> bool {
966 !self.has_pending_input() && !self.has_buffered_output()
967 }
968
969 #[must_use]
973 pub fn into_inner(mut self) -> W {
974 self.take_inner()
975 }
976
977 #[allow(clippy::result_large_err)]
983 pub fn try_into_inner(mut self) -> Result<W, Self> {
984 if !self.can_into_inner() {
985 return Err(self);
986 }
987 Ok(self.take_inner())
988 }
989
990 fn inner_ref(&self) -> &W {
991 match &self.inner {
992 Some(inner) => inner,
993 None => unreachable!("stream encoder inner writer was already taken"),
994 }
995 }
996
997 fn inner_mut(&mut self) -> &mut W {
998 match &mut self.inner {
999 Some(inner) => inner,
1000 None => unreachable!("stream encoder inner writer was already taken"),
1001 }
1002 }
1003
1004 fn take_inner(&mut self) -> W {
1005 match self.inner.take() {
1006 Some(inner) => inner,
1007 None => unreachable!("stream encoder inner writer was already taken"),
1008 }
1009 }
1010
1011 fn clear_pending(&mut self) {
1012 crate::wipe_bytes(&mut self.pending);
1013 self.pending_len = 0;
1014 }
1015
1016 fn clear_output(&mut self) {
1017 self.output.clear_all();
1018 }
1019 }
1020
1021 impl<W, A, const PAD: bool> Drop for Encoder<W, A, PAD>
1022 where
1023 A: Alphabet,
1024 {
1025 fn drop(&mut self) {
1026 self.clear_pending();
1027 self.clear_output();
1028 }
1029 }
1030
1031 impl<W, A, const PAD: bool> core::fmt::Debug for Encoder<W, A, PAD>
1032 where
1033 A: Alphabet,
1034 {
1035 fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
1036 formatter
1037 .debug_struct("Encoder")
1038 .field("inner", &redacted_inner_state(self.inner.is_some()))
1039 .field("engine", &self.engine)
1040 .field("pending", &"<redacted>")
1041 .field("pending_len", &self.pending_len)
1042 .field("pending_input_needed_len", &self.pending_input_needed_len())
1043 .field("buffered_output_len", &self.output.len())
1044 .field("buffered_output_capacity", &self.output.capacity())
1045 .field(
1046 "buffered_output_remaining_capacity",
1047 &self.output.available_capacity(),
1048 )
1049 .field("can_into_inner", &self.can_into_inner())
1050 .field("finalized", &self.finalized)
1051 .finish()
1052 }
1053 }
1054
1055 impl<W, A, const PAD: bool> Encoder<W, A, PAD>
1056 where
1057 W: Write,
1058 A: Alphabet,
1059 {
1060 pub fn try_finish(&mut self) -> io::Result<()> {
1070 if !self.finalized {
1071 self.queue_pending_final()?;
1072 self.finalized = true;
1073 }
1074 self.flush()
1075 }
1076
1077 pub fn finish(mut self) -> io::Result<W> {
1079 self.try_finish()?;
1080 Ok(self.take_inner())
1081 }
1082
1083 fn queue_pending_final(&mut self) -> io::Result<()> {
1084 if self.pending_len == 0 {
1085 return Ok(());
1086 }
1087
1088 let mut pending = [0u8; 2];
1089 pending[..self.pending_len].copy_from_slice(&self.pending[..self.pending_len]);
1090 let pending_len = self.pending_len;
1091 let mut encoded = [0u8; 4];
1092 let result = self.queue_encoded_temp(&pending[..pending_len], &mut encoded);
1093 crate::wipe_bytes(&mut pending);
1094 result?;
1095 self.clear_pending();
1096 Ok(())
1097 }
1098
1099 fn queue_encoded_temp(&mut self, input: &[u8], encoded: &mut [u8]) -> io::Result<()> {
1100 let written = match self.engine.encode_slice(input, encoded) {
1101 Ok(written) => written,
1102 Err(err) => {
1103 crate::wipe_bytes(encoded);
1104 return Err(encode_error_to_io(err));
1105 }
1106 };
1107
1108 let result = self.output.push_slice(&encoded[..written]);
1109 crate::wipe_bytes(encoded);
1110 result
1111 }
1112
1113 fn drain_output(&mut self) -> io::Result<()> {
1114 let mut chunk = [0u8; 1024];
1115 while !self.output.is_empty() {
1116 let pending = self.output.copy_front(&mut chunk);
1117 let result = self.inner_mut().write(&chunk[..pending]);
1118 crate::wipe_bytes(&mut chunk[..pending]);
1119 match result {
1120 Ok(0) => {
1121 return Err(io::Error::new(
1122 io::ErrorKind::WriteZero,
1123 "base64 stream encoder could not drain buffered output",
1124 ));
1125 }
1126 Ok(written) => {
1127 if written > pending {
1128 return Err(io::Error::new(
1129 io::ErrorKind::InvalidData,
1130 "wrapped writer reported more bytes than provided",
1131 ));
1132 }
1133 self.output.discard_front(written);
1134 }
1135 Err(err) => return Err(err),
1136 }
1137 }
1138
1139 Ok(())
1140 }
1141 }
1142
1143 impl<W, A, const PAD: bool> Write for Encoder<W, A, PAD>
1144 where
1145 W: Write,
1146 A: Alphabet,
1147 {
1148 fn write(&mut self, input: &[u8]) -> io::Result<usize> {
1149 if input.is_empty() {
1150 self.drain_output()?;
1151 return Ok(0);
1152 }
1153 self.drain_output()?;
1154 if self.finalized {
1155 return Err(io::Error::new(
1156 io::ErrorKind::InvalidInput,
1157 "base64 stream encoder received input after finalization",
1158 ));
1159 }
1160
1161 let mut consumed = 0;
1162 if self.pending_len > 0 {
1163 let needed = 3 - self.pending_len;
1164 if input.len() < needed {
1165 self.pending[self.pending_len..self.pending_len + input.len()]
1166 .copy_from_slice(input);
1167 self.pending_len += input.len();
1168 return Ok(input.len());
1169 }
1170
1171 let mut chunk = [0u8; 3];
1172 chunk[..self.pending_len].copy_from_slice(&self.pending[..self.pending_len]);
1173 chunk[self.pending_len..].copy_from_slice(&input[..needed]);
1174
1175 let mut encoded = [0u8; 4];
1176 let result = self.queue_encoded_temp(&chunk, &mut encoded);
1177 crate::wipe_bytes(&mut chunk);
1178 result?;
1179 self.clear_pending();
1180 consumed += needed;
1181 }
1182
1183 let remaining = &input[consumed..];
1184 let full_len = remaining.len() / 3 * 3;
1185 if full_len > 0 {
1186 let max_by_queue = self.output.available_capacity() / 4 * 3;
1187 let mut take = core::cmp::min(full_len, core::cmp::min(768, max_by_queue));
1188 take -= take % 3;
1189
1190 if take == 0 {
1191 return Ok(consumed);
1192 }
1193
1194 let mut encoded = [0u8; 1024];
1195 self.queue_encoded_temp(&remaining[..take], &mut encoded)?;
1196 consumed += take;
1197
1198 if take < full_len {
1199 return Ok(consumed);
1200 }
1201 }
1202
1203 let tail = &input[consumed..];
1204 self.pending[..tail.len()].copy_from_slice(tail);
1205 self.pending_len = tail.len();
1206 consumed += tail.len();
1207
1208 Ok(consumed)
1209 }
1210
1211 fn flush(&mut self) -> io::Result<()> {
1212 self.drain_output()?;
1213 self.inner_mut().flush()
1214 }
1215 }
1216
1217 fn encode_error_to_io(err: EncodeError) -> io::Error {
1218 io::Error::new(io::ErrorKind::InvalidInput, err)
1219 }
1220
1221 pub struct Decoder<W, A, const PAD: bool>
1237 where
1238 A: Alphabet,
1239 {
1240 inner: Option<W>,
1241 engine: Engine<A, PAD>,
1242 pending: [u8; 4],
1243 pending_len: usize,
1244 output: OutputQueue<1024>,
1245 finished: bool,
1246 failed: bool,
1247 finalized: bool,
1248 }
1249
1250 impl<W, A, const PAD: bool> Decoder<W, A, PAD>
1251 where
1252 A: Alphabet,
1253 {
1254 #[must_use]
1261 pub const fn new(inner: W, engine: Engine<A, PAD>) -> Self {
1262 Self {
1263 inner: Some(inner),
1264 engine,
1265 pending: [0; 4],
1266 pending_len: 0,
1267 output: OutputQueue::new(),
1268 finished: false,
1269 finalized: false,
1270 failed: false,
1271 }
1272 }
1273
1274 #[must_use]
1276 pub fn get_ref(&self) -> &W {
1277 self.inner_ref()
1278 }
1279
1280 pub fn get_mut(&mut self) -> &mut W {
1282 self.inner_mut()
1283 }
1284
1285 #[must_use]
1287 pub const fn engine(&self) -> Engine<A, PAD> {
1288 self.engine
1289 }
1290
1291 #[must_use]
1293 pub const fn is_padded(&self) -> bool {
1294 PAD
1295 }
1296
1297 #[must_use]
1300 pub const fn pending_len(&self) -> usize {
1301 self.pending_len
1302 }
1303
1304 #[must_use]
1307 pub const fn has_pending_input(&self) -> bool {
1308 self.pending_len != 0
1309 }
1310
1311 #[must_use]
1316 pub const fn pending_input_needed_len(&self) -> usize {
1317 if self.has_pending_input() {
1318 4 - self.pending_len
1319 } else {
1320 0
1321 }
1322 }
1323
1324 #[must_use]
1327 pub const fn buffered_output_len(&self) -> usize {
1328 self.output.len()
1329 }
1330
1331 #[must_use]
1334 pub const fn buffered_output_capacity(&self) -> usize {
1335 self.output.capacity()
1336 }
1337
1338 #[must_use]
1341 pub const fn buffered_output_remaining_capacity(&self) -> usize {
1342 self.output.available_capacity()
1343 }
1344
1345 #[must_use]
1348 pub const fn has_buffered_output(&self) -> bool {
1349 !self.output.is_empty()
1350 }
1351
1352 #[must_use]
1358 pub const fn has_terminal_padding(&self) -> bool {
1359 self.finished
1360 }
1361
1362 #[must_use]
1366 pub const fn is_finalized(&self) -> bool {
1367 self.finalized
1368 }
1369
1370 #[must_use]
1376 pub const fn is_failed(&self) -> bool {
1377 self.failed
1378 }
1379
1380 #[must_use]
1383 pub const fn can_into_inner(&self) -> bool {
1384 !self.is_failed() && !self.has_pending_input() && !self.has_buffered_output()
1385 }
1386
1387 #[must_use]
1391 pub fn into_inner(mut self) -> W {
1392 self.take_inner()
1393 }
1394
1395 #[allow(clippy::result_large_err)]
1401 pub fn try_into_inner(mut self) -> Result<W, Self> {
1402 if !self.can_into_inner() {
1403 return Err(self);
1404 }
1405 Ok(self.take_inner())
1406 }
1407
1408 fn inner_ref(&self) -> &W {
1409 match &self.inner {
1410 Some(inner) => inner,
1411 None => unreachable!("stream decoder inner writer was already taken"),
1412 }
1413 }
1414
1415 fn inner_mut(&mut self) -> &mut W {
1416 match &mut self.inner {
1417 Some(inner) => inner,
1418 None => unreachable!("stream decoder inner writer was already taken"),
1419 }
1420 }
1421
1422 fn take_inner(&mut self) -> W {
1423 match self.inner.take() {
1424 Some(inner) => inner,
1425 None => unreachable!("stream decoder inner writer was already taken"),
1426 }
1427 }
1428
1429 fn clear_pending(&mut self) {
1430 crate::wipe_bytes(&mut self.pending);
1431 self.pending_len = 0;
1432 }
1433
1434 fn clear_output(&mut self) {
1435 self.output.clear_all();
1436 }
1437 }
1438
1439 impl<W, A, const PAD: bool> Drop for Decoder<W, A, PAD>
1440 where
1441 A: Alphabet,
1442 {
1443 fn drop(&mut self) {
1444 self.clear_pending();
1445 self.clear_output();
1446 }
1447 }
1448
1449 impl<W, A, const PAD: bool> core::fmt::Debug for Decoder<W, A, PAD>
1450 where
1451 A: Alphabet,
1452 {
1453 fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
1454 formatter
1455 .debug_struct("Decoder")
1456 .field("inner", &redacted_inner_state(self.inner.is_some()))
1457 .field("engine", &self.engine)
1458 .field("pending", &"<redacted>")
1459 .field("pending_len", &self.pending_len)
1460 .field("pending_input_needed_len", &self.pending_input_needed_len())
1461 .field("buffered_output_len", &self.output.len())
1462 .field("buffered_output_capacity", &self.output.capacity())
1463 .field(
1464 "buffered_output_remaining_capacity",
1465 &self.output.available_capacity(),
1466 )
1467 .field("can_into_inner", &self.can_into_inner())
1468 .field("terminal_padding", &self.finished)
1469 .field("finalized", &self.finalized)
1470 .field("failed", &self.failed)
1471 .finish()
1472 }
1473 }
1474
1475 impl<W, A, const PAD: bool> Decoder<W, A, PAD>
1476 where
1477 W: Write,
1478 A: Alphabet,
1479 {
1480 pub fn try_finish(&mut self) -> io::Result<()> {
1490 if self.failed {
1491 return Err(stream_decoder_failed_error());
1492 }
1493 if !self.finalized {
1494 self.queue_pending_final()?;
1495 self.finalized = true;
1496 }
1497 self.flush()
1498 }
1499
1500 pub fn finish(mut self) -> io::Result<W> {
1502 self.try_finish()?;
1503 Ok(self.take_inner())
1504 }
1505
1506 fn queue_pending_final(&mut self) -> io::Result<()> {
1507 if self.pending_len == 0 {
1508 return Ok(());
1509 }
1510
1511 let mut pending = [0u8; 4];
1512 pending[..self.pending_len].copy_from_slice(&self.pending[..self.pending_len]);
1513 let pending_len = self.pending_len;
1514 let mut decoded = [0u8; 3];
1515 let result = self.queue_decoded_temp(&pending[..pending_len], &mut decoded);
1516 crate::wipe_bytes(&mut pending);
1517 if let Err(err) = result {
1518 self.clear_pending();
1519 return Err(err);
1520 }
1521 self.clear_pending();
1522 Ok(())
1523 }
1524
1525 fn queue_full_quad(&mut self, mut input: [u8; 4]) -> io::Result<()> {
1526 let mut decoded = [0u8; 3];
1527 let result = self.queue_decoded_temp(&input, &mut decoded);
1528 crate::wipe_bytes(&mut input);
1529 let written = result?;
1530 if written < 3 {
1531 self.finished = true;
1532 }
1533 Ok(())
1534 }
1535
1536 fn queue_decoded_temp(&mut self, input: &[u8], decoded: &mut [u8]) -> io::Result<usize> {
1537 let written = match self.engine.decode_slice(input, decoded) {
1538 Ok(written) => written,
1539 Err(err) => {
1540 crate::wipe_bytes(decoded);
1541 self.failed = true;
1542 return Err(decode_error_to_io(err));
1543 }
1544 };
1545
1546 let result = self.output.push_slice(&decoded[..written]);
1547 crate::wipe_bytes(decoded);
1548 result?;
1549 Ok(written)
1550 }
1551
1552 fn drain_output(&mut self) -> io::Result<()> {
1553 let mut chunk = [0u8; 1024];
1554 while !self.output.is_empty() {
1555 let pending = self.output.copy_front(&mut chunk);
1556 let result = self.inner_mut().write(&chunk[..pending]);
1557 crate::wipe_bytes(&mut chunk[..pending]);
1558 match result {
1559 Ok(0) => {
1560 return Err(io::Error::new(
1561 io::ErrorKind::WriteZero,
1562 "base64 stream decoder could not drain buffered output",
1563 ));
1564 }
1565 Ok(written) => {
1566 if written > pending {
1567 return Err(io::Error::new(
1568 io::ErrorKind::InvalidData,
1569 "wrapped writer reported more bytes than provided",
1570 ));
1571 }
1572 self.output.discard_front(written);
1573 }
1574 Err(err) => return Err(err),
1575 }
1576 }
1577
1578 Ok(())
1579 }
1580 }
1581
1582 impl<W, A, const PAD: bool> Write for Decoder<W, A, PAD>
1583 where
1584 W: Write,
1585 A: Alphabet,
1586 {
1587 fn write(&mut self, input: &[u8]) -> io::Result<usize> {
1588 if self.failed {
1589 return Err(stream_decoder_failed_error());
1590 }
1591 if input.is_empty() {
1592 self.drain_output()?;
1593 return Ok(0);
1594 }
1595 self.drain_output()?;
1596 if self.finalized {
1597 return Err(io::Error::new(
1598 io::ErrorKind::InvalidInput,
1599 "base64 stream decoder received input after finalization",
1600 ));
1601 }
1602 if self.finished {
1603 self.failed = true;
1604 return Err(trailing_input_after_padding_error());
1605 }
1606
1607 let mut consumed = 0;
1608 if self.pending_len > 0 {
1609 let needed = 4 - self.pending_len;
1610 if input.len() < needed {
1611 self.pending[self.pending_len..self.pending_len + input.len()]
1612 .copy_from_slice(input);
1613 self.pending_len += input.len();
1614 return Ok(input.len());
1615 }
1616
1617 let mut quad = [0u8; 4];
1618 quad[..self.pending_len].copy_from_slice(&self.pending[..self.pending_len]);
1619 quad[self.pending_len..].copy_from_slice(&input[..needed]);
1620 let result = self.queue_full_quad(quad);
1621 crate::wipe_bytes(&mut quad);
1622 if let Err(err) = result {
1623 self.clear_pending();
1624 return Err(err);
1625 }
1626 self.clear_pending();
1627 consumed += needed;
1628
1629 if self.finished {
1630 return Ok(consumed);
1631 }
1632 }
1633
1634 while input.len() - consumed >= 4 {
1635 if self.output.available_capacity() < 3 {
1636 return Ok(consumed);
1637 }
1638
1639 let mut quad = [
1640 input[consumed],
1641 input[consumed + 1],
1642 input[consumed + 2],
1643 input[consumed + 3],
1644 ];
1645 let mut decoded = [0u8; 3];
1646 let written = match self.engine.decode_slice(&quad, &mut decoded) {
1647 Ok(written) => written,
1648 Err(err) => {
1649 crate::wipe_bytes(&mut quad);
1650 crate::wipe_bytes(&mut decoded);
1651 self.failed = true;
1652 if consumed > 0 {
1653 return Ok(consumed);
1654 }
1655
1656 return Err(decode_error_to_io(err));
1657 }
1658 };
1659
1660 let result = self.output.push_slice(&decoded[..written]);
1661 crate::wipe_bytes(&mut quad);
1662 crate::wipe_bytes(&mut decoded);
1663 result?;
1664 consumed += 4;
1665
1666 if written < 3 {
1667 self.finished = true;
1668 return Ok(consumed);
1669 }
1670 }
1671
1672 let tail = &input[consumed..];
1673 self.pending[..tail.len()].copy_from_slice(tail);
1674 self.pending_len = tail.len();
1675 consumed += tail.len();
1676
1677 Ok(consumed)
1678 }
1679
1680 fn flush(&mut self) -> io::Result<()> {
1681 if self.failed {
1682 return Err(stream_decoder_failed_error());
1683 }
1684 self.drain_output()?;
1685 self.inner_mut().flush()
1686 }
1687 }
1688
1689 fn decode_error_to_io(err: DecodeError) -> io::Error {
1690 io::Error::new(io::ErrorKind::InvalidInput, err)
1691 }
1692
1693 fn trailing_input_after_padding_error() -> io::Error {
1694 io::Error::new(
1695 io::ErrorKind::InvalidInput,
1696 "base64 decoder received trailing input after padding",
1697 )
1698 }
1699
1700 fn stream_decoder_failed_error() -> io::Error {
1701 io::Error::new(
1702 io::ErrorKind::InvalidInput,
1703 "base64 stream decoder is failed after malformed input",
1704 )
1705 }
1706
1707 fn stream_encoder_failed_error() -> io::Error {
1708 io::Error::new(
1709 io::ErrorKind::InvalidInput,
1710 "base64 stream encoder is failed after internal error",
1711 )
1712 }
1713
1714 pub struct DecoderReader<R, A, const PAD: bool>
1729 where
1730 A: Alphabet,
1731 {
1732 inner: Option<R>,
1733 engine: Engine<A, PAD>,
1734 pending: [u8; 4],
1735 pending_len: usize,
1736 output: OutputQueue<3>,
1737 finished: bool,
1738 terminal_seen: bool,
1739 failed: bool,
1740 }
1741
1742 impl<R, A, const PAD: bool> DecoderReader<R, A, PAD>
1743 where
1744 A: Alphabet,
1745 {
1746 #[must_use]
1753 pub fn new(inner: R, engine: Engine<A, PAD>) -> Self {
1754 Self {
1755 inner: Some(inner),
1756 engine,
1757 pending: [0; 4],
1758 pending_len: 0,
1759 output: OutputQueue::new(),
1760 finished: false,
1761 terminal_seen: false,
1762 failed: false,
1763 }
1764 }
1765
1766 #[must_use]
1768 pub fn get_ref(&self) -> &R {
1769 self.inner_ref()
1770 }
1771
1772 pub fn get_mut(&mut self) -> &mut R {
1774 self.inner_mut()
1775 }
1776
1777 #[must_use]
1779 pub const fn engine(&self) -> Engine<A, PAD> {
1780 self.engine
1781 }
1782
1783 #[must_use]
1785 pub const fn is_padded(&self) -> bool {
1786 PAD
1787 }
1788
1789 #[must_use]
1792 pub const fn pending_len(&self) -> usize {
1793 self.pending_len
1794 }
1795
1796 #[must_use]
1799 pub const fn has_pending_input(&self) -> bool {
1800 self.pending_len != 0
1801 }
1802
1803 #[must_use]
1808 pub const fn pending_input_needed_len(&self) -> usize {
1809 if self.has_pending_input() {
1810 4 - self.pending_len
1811 } else {
1812 0
1813 }
1814 }
1815
1816 #[must_use]
1819 pub const fn buffered_output_len(&self) -> usize {
1820 self.output.len()
1821 }
1822
1823 #[must_use]
1826 pub const fn buffered_output_capacity(&self) -> usize {
1827 self.output.capacity()
1828 }
1829
1830 #[must_use]
1833 pub const fn buffered_output_remaining_capacity(&self) -> usize {
1834 self.output.available_capacity()
1835 }
1836
1837 #[must_use]
1840 pub const fn has_buffered_output(&self) -> bool {
1841 !self.output.is_empty()
1842 }
1843
1844 #[must_use]
1851 pub const fn has_terminal_padding(&self) -> bool {
1852 self.terminal_seen
1853 }
1854
1855 #[must_use]
1861 pub const fn has_finished_input(&self) -> bool {
1862 self.finished
1863 }
1864
1865 #[must_use]
1868 pub const fn is_finished(&self) -> bool {
1869 self.finished && self.output.is_empty()
1870 }
1871
1872 #[must_use]
1879 pub const fn is_failed(&self) -> bool {
1880 self.failed
1881 }
1882
1883 #[must_use]
1886 pub const fn can_into_inner(&self) -> bool {
1887 !self.is_failed() && self.is_finished()
1888 }
1889
1890 #[must_use]
1892 pub fn into_inner(mut self) -> R {
1893 self.take_inner()
1894 }
1895
1896 #[allow(clippy::result_large_err)]
1904 pub fn try_into_inner(mut self) -> Result<R, Self> {
1905 if !self.can_into_inner() {
1906 return Err(self);
1907 }
1908 Ok(self.take_inner())
1909 }
1910
1911 fn inner_ref(&self) -> &R {
1912 match &self.inner {
1913 Some(inner) => inner,
1914 None => unreachable!("stream decoder reader inner reader was already taken"),
1915 }
1916 }
1917
1918 fn inner_mut(&mut self) -> &mut R {
1919 match &mut self.inner {
1920 Some(inner) => inner,
1921 None => unreachable!("stream decoder reader inner reader was already taken"),
1922 }
1923 }
1924
1925 fn take_inner(&mut self) -> R {
1926 match self.inner.take() {
1927 Some(inner) => inner,
1928 None => unreachable!("stream decoder reader inner reader was already taken"),
1929 }
1930 }
1931
1932 fn clear_pending(&mut self) {
1933 crate::wipe_bytes(&mut self.pending);
1934 self.pending_len = 0;
1935 }
1936 }
1937
1938 impl<R, A, const PAD: bool> Drop for DecoderReader<R, A, PAD>
1939 where
1940 A: Alphabet,
1941 {
1942 fn drop(&mut self) {
1943 self.clear_pending();
1944 self.output.clear_all();
1945 }
1946 }
1947
1948 impl<R, A, const PAD: bool> core::fmt::Debug for DecoderReader<R, A, PAD>
1949 where
1950 A: Alphabet,
1951 {
1952 fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
1953 formatter
1954 .debug_struct("DecoderReader")
1955 .field("inner", &redacted_inner_state(self.inner.is_some()))
1956 .field("engine", &self.engine)
1957 .field("pending", &"<redacted>")
1958 .field("pending_len", &self.pending_len)
1959 .field("pending_input_needed_len", &self.pending_input_needed_len())
1960 .field("buffered_output_len", &self.output.len())
1961 .field("buffered_output_capacity", &self.output.capacity())
1962 .field(
1963 "buffered_output_remaining_capacity",
1964 &self.output.available_capacity(),
1965 )
1966 .field("can_into_inner", &self.can_into_inner())
1967 .field("finished", &self.finished)
1968 .field("terminal_padding", &self.terminal_seen)
1969 .field("failed", &self.failed)
1970 .finish()
1971 }
1972 }
1973
1974 impl<R, A, const PAD: bool> Read for DecoderReader<R, A, PAD>
1975 where
1976 R: Read,
1977 A: Alphabet,
1978 {
1979 fn read(&mut self, output: &mut [u8]) -> io::Result<usize> {
1980 if output.is_empty() {
1981 return Ok(0);
1982 }
1983 if self.failed {
1984 return Err(stream_decoder_failed_error());
1985 }
1986
1987 while self.output.is_empty() && !self.finished {
1988 self.fill_output()?;
1989 }
1990
1991 Ok(self.output.pop_slice(output))
1992 }
1993 }
1994
1995 impl<R, A, const PAD: bool> DecoderReader<R, A, PAD>
1996 where
1997 R: Read,
1998 A: Alphabet,
1999 {
2000 fn fill_output(&mut self) -> io::Result<()> {
2001 if self.failed {
2002 return Err(stream_decoder_failed_error());
2003 }
2004 if self.terminal_seen {
2005 self.finished = true;
2006 return Ok(());
2007 }
2008
2009 let mut input = [0u8; 4];
2010 let available = 4 - self.pending_len;
2011 let read = match self.inner_mut().read(&mut input[..available]) {
2012 Ok(read) => read,
2013 Err(err) => {
2014 crate::wipe_bytes(&mut input);
2015 return Err(err);
2016 }
2017 };
2018 if read == 0 {
2019 crate::wipe_bytes(&mut input);
2020 self.finished = true;
2021 self.push_final_pending()?;
2022 return Ok(());
2023 }
2024
2025 self.pending[self.pending_len..self.pending_len + read].copy_from_slice(&input[..read]);
2026 crate::wipe_bytes(&mut input);
2027 self.pending_len += read;
2028 if self.pending_len < 4 {
2029 return Ok(());
2030 }
2031
2032 let mut quad = self.pending;
2033 self.clear_pending();
2034 let result = self.push_decoded(&quad);
2035 crate::wipe_bytes(&mut quad);
2036 result?;
2037 if self.terminal_seen {
2038 self.finished = true;
2039 }
2040 Ok(())
2041 }
2042
2043 fn push_final_pending(&mut self) -> io::Result<()> {
2044 if self.pending_len == 0 {
2045 return Ok(());
2046 }
2047
2048 let mut pending = [0u8; 4];
2049 pending[..self.pending_len].copy_from_slice(&self.pending[..self.pending_len]);
2050 let pending_len = self.pending_len;
2051 self.clear_pending();
2052 let result = self.push_decoded(&pending[..pending_len]);
2053 crate::wipe_bytes(&mut pending);
2054 result
2055 }
2056
2057 fn push_decoded(&mut self, input: &[u8]) -> io::Result<()> {
2058 let mut decoded = [0u8; 3];
2059 let written = match self.engine.decode_slice(input, &mut decoded) {
2060 Ok(written) => written,
2061 Err(err) => {
2062 crate::wipe_bytes(&mut decoded);
2063 self.failed = true;
2064 return Err(decode_error_to_io(err));
2065 }
2066 };
2067 let result = self.output.push_slice(&decoded[..written]);
2068 crate::wipe_bytes(&mut decoded);
2069 result?;
2070 if input.len() == 4 && written < 3 {
2071 self.terminal_seen = true;
2072 }
2073 Ok(())
2074 }
2075 }
2076
2077 pub struct EncoderReader<R, A, const PAD: bool>
2079 where
2080 A: Alphabet,
2081 {
2082 inner: Option<R>,
2083 engine: Engine<A, PAD>,
2084 pending: [u8; 2],
2085 pending_len: usize,
2086 output: OutputQueue<1024>,
2087 finished: bool,
2088 failed: bool,
2089 }
2090
2091 impl<R, A, const PAD: bool> EncoderReader<R, A, PAD>
2092 where
2093 A: Alphabet,
2094 {
2095 #[must_use]
2097 pub fn new(inner: R, engine: Engine<A, PAD>) -> Self {
2098 Self {
2099 inner: Some(inner),
2100 engine,
2101 pending: [0; 2],
2102 pending_len: 0,
2103 output: OutputQueue::new(),
2104 finished: false,
2105 failed: false,
2106 }
2107 }
2108
2109 #[must_use]
2111 pub fn get_ref(&self) -> &R {
2112 self.inner_ref()
2113 }
2114
2115 pub fn get_mut(&mut self) -> &mut R {
2117 self.inner_mut()
2118 }
2119
2120 #[must_use]
2122 pub const fn engine(&self) -> Engine<A, PAD> {
2123 self.engine
2124 }
2125
2126 #[must_use]
2128 pub const fn is_padded(&self) -> bool {
2129 PAD
2130 }
2131
2132 #[must_use]
2135 pub const fn pending_len(&self) -> usize {
2136 self.pending_len
2137 }
2138
2139 #[must_use]
2142 pub const fn has_pending_input(&self) -> bool {
2143 self.pending_len != 0
2144 }
2145
2146 #[must_use]
2151 pub const fn pending_input_needed_len(&self) -> usize {
2152 if self.has_pending_input() {
2153 3 - self.pending_len
2154 } else {
2155 0
2156 }
2157 }
2158
2159 #[must_use]
2162 pub const fn buffered_output_len(&self) -> usize {
2163 self.output.len()
2164 }
2165
2166 #[must_use]
2169 pub const fn buffered_output_capacity(&self) -> usize {
2170 self.output.capacity()
2171 }
2172
2173 #[must_use]
2176 pub const fn buffered_output_remaining_capacity(&self) -> usize {
2177 self.output.available_capacity()
2178 }
2179
2180 #[must_use]
2183 pub const fn has_buffered_output(&self) -> bool {
2184 !self.output.is_empty()
2185 }
2186
2187 #[must_use]
2193 pub const fn has_finished_input(&self) -> bool {
2194 self.finished
2195 }
2196
2197 #[must_use]
2200 pub const fn is_finished(&self) -> bool {
2201 self.finished && self.output.is_empty()
2202 }
2203
2204 #[must_use]
2207 pub const fn is_failed(&self) -> bool {
2208 self.failed
2209 }
2210
2211 #[must_use]
2214 pub const fn can_into_inner(&self) -> bool {
2215 self.is_finished() && !self.failed
2216 }
2217
2218 #[must_use]
2220 pub fn into_inner(mut self) -> R {
2221 self.take_inner()
2222 }
2223
2224 #[allow(clippy::result_large_err)]
2231 pub fn try_into_inner(mut self) -> Result<R, Self> {
2232 if !self.can_into_inner() {
2233 return Err(self);
2234 }
2235 Ok(self.take_inner())
2236 }
2237
2238 fn inner_ref(&self) -> &R {
2239 match &self.inner {
2240 Some(inner) => inner,
2241 None => unreachable!("stream encoder reader inner reader was already taken"),
2242 }
2243 }
2244
2245 fn inner_mut(&mut self) -> &mut R {
2246 match &mut self.inner {
2247 Some(inner) => inner,
2248 None => unreachable!("stream encoder reader inner reader was already taken"),
2249 }
2250 }
2251
2252 fn take_inner(&mut self) -> R {
2253 match self.inner.take() {
2254 Some(inner) => inner,
2255 None => unreachable!("stream encoder reader inner reader was already taken"),
2256 }
2257 }
2258
2259 fn clear_pending(&mut self) {
2260 crate::wipe_bytes(&mut self.pending);
2261 self.pending_len = 0;
2262 }
2263 }
2264
2265 impl<R, A, const PAD: bool> Drop for EncoderReader<R, A, PAD>
2266 where
2267 A: Alphabet,
2268 {
2269 fn drop(&mut self) {
2270 self.clear_pending();
2271 self.output.clear_all();
2272 }
2273 }
2274
2275 impl<R, A, const PAD: bool> core::fmt::Debug for EncoderReader<R, A, PAD>
2276 where
2277 A: Alphabet,
2278 {
2279 fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
2280 formatter
2281 .debug_struct("EncoderReader")
2282 .field("inner", &redacted_inner_state(self.inner.is_some()))
2283 .field("engine", &self.engine)
2284 .field("pending", &"<redacted>")
2285 .field("pending_len", &self.pending_len)
2286 .field("pending_input_needed_len", &self.pending_input_needed_len())
2287 .field("buffered_output_len", &self.output.len())
2288 .field("buffered_output_capacity", &self.output.capacity())
2289 .field(
2290 "buffered_output_remaining_capacity",
2291 &self.output.available_capacity(),
2292 )
2293 .field("can_into_inner", &self.can_into_inner())
2294 .field("finished", &self.finished)
2295 .field("failed", &self.failed)
2296 .finish()
2297 }
2298 }
2299
2300 impl<R, A, const PAD: bool> Read for EncoderReader<R, A, PAD>
2301 where
2302 R: Read,
2303 A: Alphabet,
2304 {
2305 fn read(&mut self, output: &mut [u8]) -> io::Result<usize> {
2306 if self.failed {
2307 return Err(stream_encoder_failed_error());
2308 }
2309
2310 if output.is_empty() {
2311 return Ok(0);
2312 }
2313
2314 while self.output.is_empty() && !self.finished {
2315 self.fill_output()?;
2316 }
2317
2318 Ok(self.output.pop_slice(output))
2319 }
2320 }
2321
2322 impl<R, A, const PAD: bool> EncoderReader<R, A, PAD>
2323 where
2324 R: Read,
2325 A: Alphabet,
2326 {
2327 fn fill_output(&mut self) -> io::Result<()> {
2328 let mut input = [0u8; 768];
2329 let read = match self.inner_mut().read(&mut input) {
2330 Ok(read) => read,
2331 Err(err) => {
2332 crate::wipe_bytes(&mut input);
2333 return Err(err);
2334 }
2335 };
2336 if read == 0 {
2337 crate::wipe_bytes(&mut input);
2338 self.finished = true;
2339 if let Err(err) = self.push_final_pending() {
2340 self.failed = true;
2341 return Err(err);
2342 }
2343 return Ok(());
2344 }
2345
2346 let mut consumed = 0;
2347 if self.pending_len > 0 {
2348 let needed = 3 - self.pending_len;
2349 if read < needed {
2350 self.pending[self.pending_len..self.pending_len + read]
2351 .copy_from_slice(&input[..read]);
2352 self.pending_len += read;
2353 crate::wipe_bytes(&mut input);
2354 return Ok(());
2355 }
2356
2357 let mut chunk = [0u8; 3];
2358 chunk[..self.pending_len].copy_from_slice(&self.pending[..self.pending_len]);
2359 chunk[self.pending_len..].copy_from_slice(&input[..needed]);
2360 let result = self.push_encoded(&chunk);
2361 crate::wipe_bytes(&mut chunk);
2362 if let Err(err) = result {
2363 crate::wipe_bytes(&mut input);
2364 self.failed = true;
2365 return Err(err);
2366 }
2367 self.clear_pending();
2368 consumed += needed;
2369 }
2370
2371 let remaining = &input[consumed..read];
2372 let full_len = remaining.len() / 3 * 3;
2373 let tail_len = remaining.len() - full_len;
2374 let mut tail = [0u8; 2];
2375 tail[..tail_len].copy_from_slice(&remaining[full_len..]);
2376 let result = if full_len > 0 {
2377 self.push_encoded(&remaining[..full_len])
2378 } else {
2379 Ok(())
2380 };
2381 crate::wipe_bytes(&mut input);
2382 if let Err(err) = result {
2383 crate::wipe_bytes(&mut tail);
2384 self.failed = true;
2385 return Err(err);
2386 }
2387 self.pending[..tail_len].copy_from_slice(&tail[..tail_len]);
2388 crate::wipe_bytes(&mut tail);
2389 self.pending_len = tail_len;
2390 Ok(())
2391 }
2392
2393 fn push_final_pending(&mut self) -> io::Result<()> {
2394 if self.pending_len == 0 {
2395 return Ok(());
2396 }
2397
2398 let mut pending = [0u8; 2];
2399 pending[..self.pending_len].copy_from_slice(&self.pending[..self.pending_len]);
2400 let pending_len = self.pending_len;
2401 self.clear_pending();
2402 let result = self.push_encoded(&pending[..pending_len]);
2403 crate::wipe_bytes(&mut pending);
2404 result
2405 }
2406
2407 fn push_encoded(&mut self, input: &[u8]) -> io::Result<()> {
2408 let mut encoded = [0u8; 1024];
2409 let written = match self.engine.encode_slice(input, &mut encoded) {
2410 Ok(written) => written,
2411 Err(err) => {
2412 crate::wipe_bytes(&mut encoded);
2413 return Err(encode_error_to_io(err));
2414 }
2415 };
2416 let result = self.output.push_slice(&encoded[..written]);
2417 crate::wipe_bytes(&mut encoded);
2418 result
2419 }
2420 }
2421
2422 const fn redacted_inner_state(present: bool) -> &'static str {
2423 if present { "<present>" } else { "<taken>" }
2424 }
2425}
2426
2427pub mod ct {
2469 use super::{
2470 Alphabet, DecodeError, DecodedBuffer, Standard, UrlSafe, ct_decode_in_place,
2471 ct_decode_slice, ct_decode_slice_staged_clear_tail, ct_decoded_len, ct_validate_decode,
2472 };
2473 use core::marker::PhantomData;
2474
2475 pub const STANDARD: CtEngine<Standard, true> = CtEngine::new();
2477
2478 pub const STANDARD_NO_PAD: CtEngine<Standard, false> = CtEngine::new();
2480
2481 pub const URL_SAFE: CtEngine<UrlSafe, true> = CtEngine::new();
2483
2484 pub const URL_SAFE_NO_PAD: CtEngine<UrlSafe, false> = CtEngine::new();
2486
2487 #[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
2489 pub struct CtEngine<A, const PAD: bool> {
2490 alphabet: PhantomData<A>,
2491 }
2492
2493 impl<A, const PAD: bool> CtEngine<A, PAD>
2494 where
2495 A: Alphabet,
2496 {
2497 #[must_use]
2499 pub const fn new() -> Self {
2500 Self {
2501 alphabet: PhantomData,
2502 }
2503 }
2504
2505 #[must_use]
2508 pub const fn is_padded(&self) -> bool {
2509 PAD
2510 }
2511
2512 pub fn validate_result(&self, input: &[u8]) -> Result<(), DecodeError> {
2528 ct_validate_decode::<A, PAD>(input)
2529 }
2530
2531 #[must_use]
2545 pub fn validate(&self, input: &[u8]) -> bool {
2546 self.validate_result(input).is_ok()
2547 }
2548
2549 pub fn decoded_len(&self, input: &[u8]) -> Result<usize, DecodeError> {
2555 ct_decoded_len::<A, PAD>(input)
2556 }
2557
2558 #[must_use = "handle decode errors; use decode_slice_staged_clear_tail for shared-memory or HSM-style threat models"]
2592 pub fn decode_slice_clear_tail(
2593 &self,
2594 input: &[u8],
2595 output: &mut [u8],
2596 ) -> Result<usize, DecodeError> {
2597 let written = match ct_decode_slice::<A, PAD>(input, output) {
2598 Ok(written) => written,
2599 Err(err) => {
2600 crate::wipe_bytes(output);
2601 return Err(err);
2602 }
2603 };
2604 crate::wipe_tail(output, written);
2605 Ok(written)
2606 }
2607
2608 pub fn decode_slice_staged_clear_tail(
2622 &self,
2623 input: &[u8],
2624 output: &mut [u8],
2625 staging: &mut [u8],
2626 ) -> Result<usize, DecodeError> {
2627 ct_decode_slice_staged_clear_tail::<A, PAD>(input, output, staging)
2628 }
2629
2630 pub fn decode_buffer<const CAP: usize>(
2646 &self,
2647 input: &[u8],
2648 ) -> Result<DecodedBuffer<CAP>, DecodeError> {
2649 let mut output = DecodedBuffer::new();
2650 let written = match self.decode_slice_clear_tail(input, &mut output.bytes) {
2651 Ok(written) => written,
2652 Err(err) => {
2653 output.clear();
2654 return Err(err);
2655 }
2656 };
2657 output.len = written;
2658 Ok(output)
2659 }
2660
2661 pub fn decode_in_place_clear_tail<'a>(
2689 &self,
2690 buffer: &'a mut [u8],
2691 ) -> Result<&'a mut [u8], DecodeError> {
2692 let len = match ct_decode_in_place::<A, PAD>(buffer) {
2693 Ok(len) => len,
2694 Err(err) => {
2695 crate::wipe_bytes(buffer);
2696 return Err(err);
2697 }
2698 };
2699 crate::wipe_tail(buffer, len);
2700 Ok(&mut buffer[..len])
2701 }
2702 }
2703
2704 impl<A, const PAD: bool> core::fmt::Display for CtEngine<A, PAD> {
2705 fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
2706 write!(formatter, "ct padded={PAD}")
2707 }
2708 }
2709}
2710
2711#[doc(alias = "ct")]
2717#[doc(alias = "constant_time")]
2718#[doc(alias = "sensitive")]
2719pub const STANDARD: Engine<Standard, true> = Engine::new();
2720
2721#[doc(alias = "ct")]
2728#[doc(alias = "constant_time")]
2729#[doc(alias = "sensitive")]
2730pub const STANDARD_NO_PAD: Engine<Standard, false> = Engine::new();
2731
2732#[doc(alias = "ct")]
2738#[doc(alias = "constant_time")]
2739#[doc(alias = "sensitive")]
2740pub const URL_SAFE: Engine<UrlSafe, true> = Engine::new();
2741
2742#[doc(alias = "ct")]
2749#[doc(alias = "constant_time")]
2750#[doc(alias = "sensitive")]
2751pub const URL_SAFE_NO_PAD: Engine<UrlSafe, false> = Engine::new();
2752
2753#[doc(alias = "ct")]
2761#[doc(alias = "constant_time")]
2762#[doc(alias = "sensitive")]
2763pub const BCRYPT_NO_PAD: Engine<Bcrypt, false> = Engine::new();
2764
2765#[doc(alias = "ct")]
2773#[doc(alias = "constant_time")]
2774#[doc(alias = "sensitive")]
2775pub const CRYPT_NO_PAD: Engine<Crypt, false> = Engine::new();
2776
2777#[derive(Clone, Copy, Debug, Eq, PartialEq)]
2779pub enum LineEnding {
2780 Lf,
2782 CrLf,
2784}
2785
2786impl LineEnding {
2787 #[must_use]
2789 pub const fn name(self) -> &'static str {
2790 match self {
2791 Self::Lf => "LF",
2792 Self::CrLf => "CRLF",
2793 }
2794 }
2795
2796 #[must_use]
2798 pub const fn as_str(self) -> &'static str {
2799 match self {
2800 Self::Lf => "\n",
2801 Self::CrLf => "\r\n",
2802 }
2803 }
2804
2805 #[must_use]
2807 pub const fn as_bytes(self) -> &'static [u8] {
2808 self.as_str().as_bytes()
2809 }
2810
2811 #[must_use]
2813 pub const fn byte_len(self) -> usize {
2814 match self {
2815 Self::Lf => 1,
2816 Self::CrLf => 2,
2817 }
2818 }
2819}
2820
2821impl core::fmt::Display for LineEnding {
2822 fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
2823 formatter.write_str(self.name())
2824 }
2825}
2826
2827#[derive(Clone, Copy, Debug, Eq, PartialEq)]
2833pub struct LineWrap {
2834 pub line_len: usize,
2836 pub line_ending: LineEnding,
2838}
2839
2840impl LineWrap {
2841 pub const MIME: Self = Self::new(76, LineEnding::CrLf);
2843 pub const PEM: Self = Self::new(64, LineEnding::Lf);
2845 pub const PEM_CRLF: Self = Self::new(64, LineEnding::CrLf);
2847
2848 #[must_use]
2863 pub const fn new(line_len: usize, line_ending: LineEnding) -> Self {
2864 assert!(line_len != 0, "base64 line wrap length must be non-zero");
2865 Self {
2866 line_len,
2867 line_ending,
2868 }
2869 }
2870
2871 #[must_use]
2878 pub const fn checked_new(line_len: usize, line_ending: LineEnding) -> Option<Self> {
2879 if line_len == 0 {
2880 None
2881 } else {
2882 Some(Self::new(line_len, line_ending))
2883 }
2884 }
2885
2886 #[must_use]
2888 pub const fn line_len(self) -> usize {
2889 self.line_len
2890 }
2891
2892 #[must_use]
2894 pub const fn line_ending(self) -> LineEnding {
2895 self.line_ending
2896 }
2897
2898 #[must_use]
2900 pub const fn is_valid(self) -> bool {
2901 self.line_len != 0
2902 }
2903}
2904
2905impl core::fmt::Display for LineWrap {
2906 fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
2907 write!(formatter, "{}:{}", self.line_len, self.line_ending.name())
2908 }
2909}
2910
2911#[inline(never)]
2912#[allow(unsafe_code)]
2913fn wipe_bytes(bytes: &mut [u8]) {
2914 for byte in bytes.iter_mut() {
2915 unsafe {
2919 core::ptr::write_volatile(byte, 0);
2920 }
2921 }
2922 wipe_barrier(bytes.as_mut_ptr(), bytes.len());
2923}
2924
2925#[inline(never)]
2926#[allow(unsafe_code)]
2927fn wipe_barrier(ptr: *mut u8, len: usize) {
2928 let _ = (ptr, len);
2929
2930 #[cfg(all(not(miri), any(target_arch = "x86", target_arch = "x86_64")))]
2931 {
2932 unsafe {
2936 core::arch::asm!(
2937 "mfence",
2938 "/* {0} {1} */",
2939 in(reg) ptr,
2940 in(reg) len,
2941 options(nostack, preserves_flags)
2942 );
2943 }
2944 }
2945
2946 #[cfg(all(not(miri), target_arch = "aarch64"))]
2947 {
2948 unsafe {
2952 core::arch::asm!(
2953 "dsb sy",
2954 "isb sy",
2955 "hint #20",
2956 "/* {0} {1} */",
2957 in(reg) ptr,
2958 in(reg) len,
2959 options(nostack, preserves_flags)
2960 );
2961 }
2962 }
2963
2964 #[cfg(all(not(miri), target_arch = "arm"))]
2965 {
2966 unsafe {
2970 core::arch::asm!(
2971 "dsb sy",
2972 "isb sy",
2973 "/* {0} {1} */",
2974 in(reg) ptr,
2975 in(reg) len,
2976 options(nostack, preserves_flags)
2977 );
2978 }
2979 }
2980
2981 #[cfg(all(not(miri), any(target_arch = "riscv32", target_arch = "riscv64")))]
2982 {
2983 unsafe {
2986 core::arch::asm!(
2987 "fence rw, rw",
2988 "/* {0} {1} */",
2989 in(reg) ptr,
2990 in(reg) len,
2991 options(nostack, preserves_flags)
2992 );
2993 }
2994 }
2995
2996 core::sync::atomic::compiler_fence(core::sync::atomic::Ordering::SeqCst);
2997}
2998
2999fn wipe_tail(bytes: &mut [u8], start: usize) {
3000 wipe_bytes(&mut bytes[start..]);
3001}
3002
3003#[cfg(feature = "alloc")]
3004#[allow(unsafe_code)]
3005fn wipe_vec_spare_capacity(bytes: &mut alloc::vec::Vec<u8>) {
3006 let ptr = bytes.as_mut_ptr();
3007 let len = bytes.len();
3008 let capacity = bytes.capacity();
3009 let spare = capacity - len;
3010 if spare == 0 {
3011 return;
3012 }
3013
3014 let mut offset = len;
3015 while offset < capacity {
3016 unsafe {
3020 core::ptr::write_volatile(ptr.add(offset), 0);
3021 }
3022 offset += 1;
3023 }
3024 let spare_ptr = unsafe { ptr.add(len) };
3027 wipe_barrier(spare_ptr, spare);
3028}
3029
3030#[cfg(feature = "alloc")]
3031fn wipe_vec_all(bytes: &mut alloc::vec::Vec<u8>) {
3032 wipe_bytes(bytes);
3033 wipe_vec_spare_capacity(bytes);
3034}
3035
3036pub struct EncodedBuffer<const CAP: usize> {
3053 bytes: [u8; CAP],
3054 len: usize,
3055}
3056
3057pub struct ExposedEncodedArray<const CAP: usize> {
3064 bytes: [u8; CAP],
3065 len: usize,
3066}
3067
3068impl<const CAP: usize> ExposedEncodedArray<CAP> {
3069 #[must_use]
3075 pub const fn from_array(bytes: [u8; CAP], len: usize) -> Self {
3076 assert!(len <= CAP, "visible length exceeds array capacity");
3077 Self { bytes, len }
3078 }
3079
3080 #[must_use]
3082 pub fn as_bytes(&self) -> &[u8] {
3083 &self.bytes[..self.len]
3084 }
3085
3086 #[must_use]
3088 pub const fn len(&self) -> usize {
3089 self.len
3090 }
3091
3092 #[must_use]
3094 pub const fn is_empty(&self) -> bool {
3095 self.len == 0
3096 }
3097
3098 #[must_use]
3100 pub const fn capacity(&self) -> usize {
3101 CAP
3102 }
3103
3104 #[must_use = "caller must zeroize the returned array"]
3116 pub fn into_exposed_unprotected_array_caller_must_zeroize(mut self) -> ([u8; CAP], usize) {
3117 let len = self.len;
3118 self.len = 0;
3119 (core::mem::replace(&mut self.bytes, [0u8; CAP]), len)
3120 }
3121}
3122
3123impl<const CAP: usize> Drop for ExposedEncodedArray<CAP> {
3124 fn drop(&mut self) {
3125 wipe_bytes(&mut self.bytes);
3126 self.len = 0;
3127 }
3128}
3129
3130impl<const CAP: usize> core::fmt::Debug for ExposedEncodedArray<CAP> {
3131 fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
3132 formatter
3133 .debug_struct("ExposedEncodedArray")
3134 .field("bytes", &"<redacted>")
3135 .field("len", &self.len)
3136 .field("capacity", &CAP)
3137 .finish()
3138 }
3139}
3140
3141impl<const CAP: usize> EncodedBuffer<CAP> {
3142 #[must_use]
3144 pub const fn new() -> Self {
3145 Self {
3146 bytes: [0u8; CAP],
3147 len: 0,
3148 }
3149 }
3150
3151 #[must_use]
3153 pub const fn len(&self) -> usize {
3154 self.len
3155 }
3156
3157 #[must_use]
3159 pub const fn is_empty(&self) -> bool {
3160 self.len == 0
3161 }
3162
3163 #[must_use]
3165 pub const fn is_full(&self) -> bool {
3166 self.len == CAP
3167 }
3168
3169 #[must_use]
3171 pub const fn capacity(&self) -> usize {
3172 CAP
3173 }
3174
3175 #[must_use]
3177 pub const fn remaining_capacity(&self) -> usize {
3178 CAP - self.len
3179 }
3180
3181 #[must_use]
3183 pub fn as_bytes(&self) -> &[u8] {
3184 &self.bytes[..self.len]
3185 }
3186
3187 pub fn as_utf8(&self) -> Result<&str, core::str::Utf8Error> {
3194 core::str::from_utf8(self.as_bytes())
3195 }
3196
3197 #[must_use]
3204 pub fn as_str(&self) -> &str {
3205 match self.as_utf8() {
3206 Ok(output) => output,
3207 Err(_) => unreachable!("base64 encoder produced non-UTF-8 output"),
3208 }
3209 }
3210
3211 #[doc(alias = "constant_time_eq")]
3230 #[must_use]
3231 pub fn constant_time_eq_public_len(&self, other: &[u8]) -> bool {
3232 constant_time_eq_public_len(self.as_bytes(), other)
3233 }
3234
3235 #[must_use]
3243 pub fn into_exposed_array(mut self) -> ExposedEncodedArray<CAP> {
3244 let len = self.len;
3245 self.len = 0;
3246 ExposedEncodedArray::from_array(core::mem::replace(&mut self.bytes, [0u8; CAP]), len)
3247 }
3248
3249 pub fn clear(&mut self) {
3251 wipe_bytes(&mut self.bytes);
3252 self.len = 0;
3253 }
3254
3255 pub fn clear_tail(&mut self) {
3257 wipe_tail(&mut self.bytes, self.len);
3258 }
3259}
3260
3261impl<const CAP: usize> AsRef<[u8]> for EncodedBuffer<CAP> {
3262 fn as_ref(&self) -> &[u8] {
3263 self.as_bytes()
3264 }
3265}
3266
3267impl<const CAP: usize> Clone for EncodedBuffer<CAP> {
3268 fn clone(&self) -> Self {
3278 let mut output = Self::new();
3279 output.bytes[..self.len].copy_from_slice(self.as_bytes());
3280 output.len = self.len;
3281 output
3282 }
3283}
3284
3285impl<const CAP: usize> core::fmt::Debug for EncodedBuffer<CAP> {
3286 fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
3287 formatter
3288 .debug_struct("EncodedBuffer")
3289 .field("bytes", &"<redacted>")
3290 .field("len", &self.len)
3291 .field("capacity", &CAP)
3292 .finish()
3293 }
3294}
3295
3296impl<const CAP: usize> core::fmt::Display for EncodedBuffer<CAP> {
3297 fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
3303 formatter.write_str(self.as_str())
3304 }
3305}
3306
3307impl<const CAP: usize> Default for EncodedBuffer<CAP> {
3308 fn default() -> Self {
3309 Self::new()
3310 }
3311}
3312
3313impl<const CAP: usize> Drop for EncodedBuffer<CAP> {
3314 fn drop(&mut self) {
3315 self.clear();
3316 }
3317}
3318
3319impl<const CAP: usize> TryFrom<&[u8]> for EncodedBuffer<CAP> {
3320 type Error = EncodeError;
3321
3322 fn try_from(input: &[u8]) -> Result<Self, Self::Error> {
3328 STANDARD.encode_buffer(input)
3329 }
3330}
3331
3332impl<const CAP: usize, const N: usize> TryFrom<&[u8; N]> for EncodedBuffer<CAP> {
3333 type Error = EncodeError;
3334
3335 fn try_from(input: &[u8; N]) -> Result<Self, Self::Error> {
3341 Self::try_from(&input[..])
3342 }
3343}
3344
3345impl<const CAP: usize> TryFrom<&str> for EncodedBuffer<CAP> {
3346 type Error = EncodeError;
3347
3348 fn try_from(input: &str) -> Result<Self, Self::Error> {
3355 Self::try_from(input.as_bytes())
3356 }
3357}
3358
3359pub struct DecodedBuffer<const CAP: usize> {
3376 bytes: [u8; CAP],
3377 len: usize,
3378}
3379
3380pub struct ExposedDecodedArray<const CAP: usize> {
3387 bytes: [u8; CAP],
3388 len: usize,
3389}
3390
3391impl<const CAP: usize> ExposedDecodedArray<CAP> {
3392 #[must_use]
3398 pub const fn from_array(bytes: [u8; CAP], len: usize) -> Self {
3399 assert!(len <= CAP, "visible length exceeds array capacity");
3400 Self { bytes, len }
3401 }
3402
3403 #[must_use]
3405 pub fn as_bytes(&self) -> &[u8] {
3406 &self.bytes[..self.len]
3407 }
3408
3409 #[must_use]
3411 pub const fn len(&self) -> usize {
3412 self.len
3413 }
3414
3415 #[must_use]
3417 pub const fn is_empty(&self) -> bool {
3418 self.len == 0
3419 }
3420
3421 #[must_use]
3423 pub const fn capacity(&self) -> usize {
3424 CAP
3425 }
3426
3427 #[must_use = "caller must zeroize the returned array"]
3439 pub fn into_exposed_unprotected_array_caller_must_zeroize(mut self) -> ([u8; CAP], usize) {
3440 let len = self.len;
3441 self.len = 0;
3442 (core::mem::replace(&mut self.bytes, [0u8; CAP]), len)
3443 }
3444}
3445
3446impl<const CAP: usize> Drop for ExposedDecodedArray<CAP> {
3447 fn drop(&mut self) {
3448 wipe_bytes(&mut self.bytes);
3449 self.len = 0;
3450 }
3451}
3452
3453impl<const CAP: usize> core::fmt::Debug for ExposedDecodedArray<CAP> {
3454 fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
3455 formatter
3456 .debug_struct("ExposedDecodedArray")
3457 .field("bytes", &"<redacted>")
3458 .field("len", &self.len)
3459 .field("capacity", &CAP)
3460 .finish()
3461 }
3462}
3463
3464impl<const CAP: usize> DecodedBuffer<CAP> {
3465 #[must_use]
3467 pub const fn new() -> Self {
3468 Self {
3469 bytes: [0u8; CAP],
3470 len: 0,
3471 }
3472 }
3473
3474 #[must_use]
3476 pub const fn len(&self) -> usize {
3477 self.len
3478 }
3479
3480 #[must_use]
3482 pub const fn is_empty(&self) -> bool {
3483 self.len == 0
3484 }
3485
3486 #[must_use]
3488 pub const fn is_full(&self) -> bool {
3489 self.len == CAP
3490 }
3491
3492 #[must_use]
3494 pub const fn capacity(&self) -> usize {
3495 CAP
3496 }
3497
3498 #[must_use]
3500 pub const fn remaining_capacity(&self) -> usize {
3501 CAP - self.len
3502 }
3503
3504 #[must_use]
3506 pub fn as_bytes(&self) -> &[u8] {
3507 &self.bytes[..self.len]
3508 }
3509
3510 pub fn as_utf8(&self) -> Result<&str, core::str::Utf8Error> {
3516 core::str::from_utf8(self.as_bytes())
3517 }
3518
3519 #[doc(alias = "constant_time_eq")]
3538 #[must_use]
3539 pub fn constant_time_eq_public_len(&self, other: &[u8]) -> bool {
3540 constant_time_eq_public_len(self.as_bytes(), other)
3541 }
3542
3543 #[must_use]
3551 pub fn into_exposed_array(mut self) -> ExposedDecodedArray<CAP> {
3552 let len = self.len;
3553 self.len = 0;
3554 ExposedDecodedArray::from_array(core::mem::replace(&mut self.bytes, [0u8; CAP]), len)
3555 }
3556
3557 pub fn clear(&mut self) {
3559 wipe_bytes(&mut self.bytes);
3560 self.len = 0;
3561 }
3562
3563 pub fn clear_tail(&mut self) {
3565 wipe_tail(&mut self.bytes, self.len);
3566 }
3567}
3568
3569impl<const CAP: usize> AsRef<[u8]> for DecodedBuffer<CAP> {
3570 fn as_ref(&self) -> &[u8] {
3571 self.as_bytes()
3572 }
3573}
3574
3575impl<const CAP: usize> Clone for DecodedBuffer<CAP> {
3576 fn clone(&self) -> Self {
3586 let mut output = Self::new();
3587 output.bytes[..self.len].copy_from_slice(self.as_bytes());
3588 output.len = self.len;
3589 output
3590 }
3591}
3592
3593impl<const CAP: usize> core::fmt::Debug for DecodedBuffer<CAP> {
3594 fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
3595 formatter
3596 .debug_struct("DecodedBuffer")
3597 .field("bytes", &"<redacted>")
3598 .field("len", &self.len)
3599 .field("capacity", &CAP)
3600 .finish()
3601 }
3602}
3603
3604impl<const CAP: usize> Default for DecodedBuffer<CAP> {
3605 fn default() -> Self {
3606 Self::new()
3607 }
3608}
3609
3610impl<const CAP: usize> Drop for DecodedBuffer<CAP> {
3611 fn drop(&mut self) {
3612 self.clear();
3613 }
3614}
3615
3616impl<const CAP: usize> TryFrom<&[u8]> for DecodedBuffer<CAP> {
3617 type Error = DecodeError;
3618
3619 fn try_from(input: &[u8]) -> Result<Self, Self::Error> {
3624 STANDARD.decode_buffer(input)
3625 }
3626}
3627
3628impl<const CAP: usize, const N: usize> TryFrom<&[u8; N]> for DecodedBuffer<CAP> {
3629 type Error = DecodeError;
3630
3631 fn try_from(input: &[u8; N]) -> Result<Self, Self::Error> {
3637 Self::try_from(&input[..])
3638 }
3639}
3640
3641impl<const CAP: usize> TryFrom<&str> for DecodedBuffer<CAP> {
3642 type Error = DecodeError;
3643
3644 fn try_from(input: &str) -> Result<Self, Self::Error> {
3649 Self::try_from(input.as_bytes())
3650 }
3651}
3652
3653impl<const CAP: usize> core::str::FromStr for DecodedBuffer<CAP> {
3654 type Err = DecodeError;
3655
3656 fn from_str(input: &str) -> Result<Self, Self::Err> {
3661 Self::try_from(input)
3662 }
3663}
3664
3665#[cfg(feature = "alloc")]
3693pub struct SecretBuffer {
3694 bytes: alloc::vec::Vec<u8>,
3695}
3696
3697#[cfg(feature = "alloc")]
3705pub struct ExposedSecretVec {
3706 bytes: alloc::vec::Vec<u8>,
3707}
3708
3709#[cfg(feature = "alloc")]
3710impl ExposedSecretVec {
3711 #[must_use]
3713 pub fn from_vec(mut bytes: alloc::vec::Vec<u8>) -> Self {
3714 wipe_vec_spare_capacity(&mut bytes);
3715 Self { bytes }
3716 }
3717
3718 #[must_use]
3720 pub fn len(&self) -> usize {
3721 self.bytes.len()
3722 }
3723
3724 #[must_use]
3726 pub fn is_empty(&self) -> bool {
3727 self.bytes.is_empty()
3728 }
3729
3730 #[must_use]
3735 pub fn expose_secret(&self) -> &[u8] {
3736 &self.bytes
3737 }
3738
3739 #[must_use]
3744 pub fn expose_secret_mut(&mut self) -> &mut [u8] {
3745 &mut self.bytes
3746 }
3747
3748 #[must_use = "caller must zeroize the returned Vec"]
3754 pub fn into_exposed_unprotected_vec_caller_must_zeroize(mut self) -> alloc::vec::Vec<u8> {
3755 core::mem::take(&mut self.bytes)
3756 }
3757}
3758
3759#[cfg(feature = "alloc")]
3760impl core::fmt::Debug for ExposedSecretVec {
3761 fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
3762 formatter
3763 .debug_struct("ExposedSecretVec")
3764 .field("bytes", &"<redacted>")
3765 .field("len", &self.len())
3766 .finish()
3767 }
3768}
3769
3770#[cfg(feature = "alloc")]
3771impl core::fmt::Display for ExposedSecretVec {
3772 fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
3773 formatter.write_str("<redacted>")
3774 }
3775}
3776
3777#[cfg(feature = "alloc")]
3778impl Drop for ExposedSecretVec {
3779 fn drop(&mut self) {
3780 wipe_vec_all(&mut self.bytes);
3781 }
3782}
3783
3784#[cfg(feature = "alloc")]
3785struct WipeVecGuard {
3786 bytes: alloc::vec::Vec<u8>,
3787}
3788
3789#[cfg(feature = "alloc")]
3790impl WipeVecGuard {
3791 fn from_vec(bytes: alloc::vec::Vec<u8>) -> Self {
3792 Self { bytes }
3793 }
3794
3795 fn into_validated_secret_string(mut self) -> alloc::string::String {
3796 wipe_vec_spare_capacity(&mut self.bytes);
3797 let bytes = core::mem::take(&mut self.bytes);
3798 core::mem::forget(self);
3799 string_from_validated_secret_bytes(bytes)
3800 }
3801}
3802
3803#[cfg(feature = "alloc")]
3804impl Drop for WipeVecGuard {
3805 fn drop(&mut self) {
3806 wipe_vec_all(&mut self.bytes);
3807 }
3808}
3809
3810#[cfg(feature = "alloc")]
3811impl AsRef<[u8]> for ExposedSecretVec {
3812 fn as_ref(&self) -> &[u8] {
3813 self.expose_secret()
3814 }
3815}
3816
3817#[cfg(feature = "alloc")]
3818impl AsMut<[u8]> for ExposedSecretVec {
3819 fn as_mut(&mut self) -> &mut [u8] {
3820 self.expose_secret_mut()
3821 }
3822}
3823
3824#[cfg(feature = "alloc")]
3832pub struct ExposedSecretString {
3833 text: alloc::string::String,
3834}
3835
3836#[cfg(feature = "alloc")]
3837impl ExposedSecretString {
3838 #[must_use]
3840 pub fn from_string(text: alloc::string::String) -> Self {
3841 let mut bytes = text.into_bytes();
3842 wipe_vec_spare_capacity(&mut bytes);
3843 let text = string_from_validated_secret_bytes(bytes);
3844 Self { text }
3845 }
3846
3847 #[must_use]
3849 pub fn len(&self) -> usize {
3850 self.text.len()
3851 }
3852
3853 #[must_use]
3855 pub fn is_empty(&self) -> bool {
3856 self.text.is_empty()
3857 }
3858
3859 #[must_use]
3864 pub fn expose_secret(&self) -> &str {
3865 &self.text
3866 }
3867
3868 #[must_use]
3873 pub fn expose_secret_bytes(&self) -> &[u8] {
3874 self.text.as_bytes()
3875 }
3876
3877 #[must_use = "caller must zeroize the returned String"]
3883 pub fn into_exposed_unprotected_string_caller_must_zeroize(mut self) -> alloc::string::String {
3884 core::mem::take(&mut self.text)
3885 }
3886}
3887
3888#[cfg(feature = "alloc")]
3889impl core::fmt::Debug for ExposedSecretString {
3890 fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
3891 formatter
3892 .debug_struct("ExposedSecretString")
3893 .field("text", &"<redacted>")
3894 .field("len", &self.len())
3895 .finish()
3896 }
3897}
3898
3899#[cfg(feature = "alloc")]
3900impl core::fmt::Display for ExposedSecretString {
3901 fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
3902 formatter.write_str("<redacted>")
3903 }
3904}
3905
3906#[cfg(feature = "alloc")]
3907impl Drop for ExposedSecretString {
3908 fn drop(&mut self) {
3909 let mut bytes = core::mem::take(&mut self.text).into_bytes();
3910 wipe_vec_all(&mut bytes);
3911 }
3912}
3913
3914#[cfg(feature = "alloc")]
3915impl AsRef<str> for ExposedSecretString {
3916 fn as_ref(&self) -> &str {
3917 self.expose_secret()
3918 }
3919}
3920
3921#[cfg(feature = "alloc")]
3922impl SecretBuffer {
3923 #[must_use]
3925 pub fn from_vec(mut bytes: alloc::vec::Vec<u8>) -> Self {
3926 wipe_vec_spare_capacity(&mut bytes);
3927 Self { bytes }
3928 }
3929
3930 #[must_use]
3932 pub fn from_slice(bytes: &[u8]) -> Self {
3933 Self::from_vec(bytes.to_vec())
3934 }
3935
3936 #[must_use]
3938 pub fn len(&self) -> usize {
3939 self.bytes.len()
3940 }
3941
3942 #[must_use]
3944 pub fn is_empty(&self) -> bool {
3945 self.bytes.is_empty()
3946 }
3947
3948 #[must_use]
3953 pub fn expose_secret(&self) -> &[u8] {
3954 &self.bytes
3955 }
3956
3957 pub fn expose_secret_utf8(&self) -> Result<&str, core::str::Utf8Error> {
3963 core::str::from_utf8(self.expose_secret())
3964 }
3965
3966 #[must_use]
3971 pub fn expose_secret_mut(&mut self) -> &mut [u8] {
3972 &mut self.bytes
3973 }
3974
3975 #[must_use]
3981 pub fn into_exposed_vec(mut self) -> ExposedSecretVec {
3982 ExposedSecretVec::from_vec(core::mem::take(&mut self.bytes))
3983 }
3984
3985 #[must_use = "handle invalid UTF-8 errors and keep the returned wrapper protected"]
3994 pub fn try_into_exposed_string(self) -> Result<ExposedSecretString, Self> {
3995 if core::str::from_utf8(self.expose_secret()).is_err() {
3996 return Err(self);
3997 }
3998
3999 let mut exposed = self.into_exposed_vec();
4002 let guard = WipeVecGuard::from_vec(core::mem::take(&mut exposed.bytes));
4003 drop(exposed);
4004 Ok(ExposedSecretString::from_string(
4005 guard.into_validated_secret_string(),
4006 ))
4007 }
4008
4009 #[doc(alias = "constant_time_eq")]
4028 #[must_use]
4029 pub fn constant_time_eq_public_len(&self, other: &[u8]) -> bool {
4030 constant_time_eq_public_len(self.expose_secret(), other)
4031 }
4032
4033 pub fn clear(&mut self) {
4035 wipe_vec_all(&mut self.bytes);
4036 self.bytes.clear();
4037 }
4038}
4039
4040#[cfg(feature = "alloc")]
4041impl core::fmt::Debug for SecretBuffer {
4042 fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
4043 formatter
4044 .debug_struct("SecretBuffer")
4045 .field("bytes", &"<redacted>")
4046 .field("len", &self.len())
4047 .finish()
4048 }
4049}
4050
4051#[cfg(feature = "alloc")]
4052impl core::fmt::Display for SecretBuffer {
4053 fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
4054 formatter.write_str("<redacted>")
4055 }
4056}
4057
4058#[cfg(feature = "alloc")]
4059impl Drop for SecretBuffer {
4060 fn drop(&mut self) {
4061 wipe_vec_all(&mut self.bytes);
4062 }
4063}
4064
4065#[cfg(feature = "alloc")]
4066impl From<alloc::vec::Vec<u8>> for SecretBuffer {
4067 fn from(bytes: alloc::vec::Vec<u8>) -> Self {
4072 Self::from_vec(bytes)
4073 }
4074}
4075
4076#[cfg(feature = "alloc")]
4077impl From<alloc::string::String> for SecretBuffer {
4078 fn from(text: alloc::string::String) -> Self {
4083 Self::from_vec(text.into_bytes())
4084 }
4085}
4086
4087#[cfg(feature = "alloc")]
4088impl<const CAP: usize> From<EncodedBuffer<CAP>> for SecretBuffer {
4089 fn from(buffer: EncodedBuffer<CAP>) -> Self {
4095 Self::from_slice(buffer.as_bytes())
4096 }
4097}
4098
4099#[cfg(feature = "alloc")]
4100impl<const CAP: usize> From<DecodedBuffer<CAP>> for SecretBuffer {
4101 fn from(buffer: DecodedBuffer<CAP>) -> Self {
4107 Self::from_slice(buffer.as_bytes())
4108 }
4109}
4110
4111#[cfg(feature = "alloc")]
4112impl TryFrom<&[u8]> for SecretBuffer {
4113 type Error = DecodeError;
4114
4115 fn try_from(input: &[u8]) -> Result<Self, Self::Error> {
4120 STANDARD.decode_secret(input)
4121 }
4122}
4123
4124#[cfg(feature = "alloc")]
4125impl<const N: usize> TryFrom<&[u8; N]> for SecretBuffer {
4126 type Error = DecodeError;
4127
4128 fn try_from(input: &[u8; N]) -> Result<Self, Self::Error> {
4134 Self::try_from(&input[..])
4135 }
4136}
4137
4138#[cfg(feature = "alloc")]
4139impl TryFrom<&str> for SecretBuffer {
4140 type Error = DecodeError;
4141
4142 fn try_from(input: &str) -> Result<Self, Self::Error> {
4147 Self::try_from(input.as_bytes())
4148 }
4149}
4150
4151#[cfg(feature = "alloc")]
4152impl core::str::FromStr for SecretBuffer {
4153 type Err = DecodeError;
4154
4155 fn from_str(input: &str) -> Result<Self, Self::Err> {
4160 Self::try_from(input)
4161 }
4162}
4163
4164#[derive(Clone, Copy, Debug, Eq, PartialEq)]
4170pub struct Profile<A, const PAD: bool> {
4171 engine: Engine<A, PAD>,
4172 wrap: Option<LineWrap>,
4173}
4174
4175impl<A, const PAD: bool> Profile<A, PAD>
4176where
4177 A: Alphabet,
4178{
4179 #[must_use]
4181 pub const fn new(engine: Engine<A, PAD>, wrap: Option<LineWrap>) -> Self {
4182 Self { engine, wrap }
4183 }
4184
4185 #[must_use]
4191 pub const fn checked_new(engine: Engine<A, PAD>, wrap: Option<LineWrap>) -> Option<Self> {
4192 match wrap {
4193 Some(wrap) if !wrap.is_valid() => None,
4194 _ => Some(Self::new(engine, wrap)),
4195 }
4196 }
4197
4198 #[must_use]
4200 pub const fn is_valid(&self) -> bool {
4201 match self.wrap {
4202 Some(wrap) => wrap.is_valid(),
4203 None => true,
4204 }
4205 }
4206
4207 #[must_use]
4209 pub const fn engine(&self) -> Engine<A, PAD> {
4210 self.engine
4211 }
4212
4213 #[must_use]
4215 pub const fn is_padded(&self) -> bool {
4216 PAD
4217 }
4218
4219 #[must_use]
4221 pub const fn is_wrapped(&self) -> bool {
4222 self.wrap.is_some()
4223 }
4224
4225 #[must_use]
4227 pub const fn line_wrap(&self) -> Option<LineWrap> {
4228 self.wrap
4229 }
4230
4231 #[must_use]
4233 pub const fn line_len(&self) -> Option<usize> {
4234 match self.wrap {
4235 Some(wrap) => Some(wrap.line_len()),
4236 None => None,
4237 }
4238 }
4239
4240 #[must_use]
4242 pub const fn line_ending(&self) -> Option<LineEnding> {
4243 match self.wrap {
4244 Some(wrap) => Some(wrap.line_ending()),
4245 None => None,
4246 }
4247 }
4248
4249 pub const fn encoded_len(&self, input_len: usize) -> Result<usize, EncodeError> {
4251 match self.wrap {
4252 Some(wrap) => wrapped_encoded_len(input_len, PAD, wrap),
4253 None => encoded_len(input_len, PAD),
4254 }
4255 }
4256
4257 #[must_use]
4260 pub const fn checked_encoded_len(&self, input_len: usize) -> Option<usize> {
4261 match self.wrap {
4262 Some(wrap) => checked_wrapped_encoded_len(input_len, PAD, wrap),
4263 None => checked_encoded_len(input_len, PAD),
4264 }
4265 }
4266
4267 pub fn decoded_len(&self, input: &[u8]) -> Result<usize, DecodeError> {
4269 match self.wrap {
4270 Some(wrap) => self.engine.decoded_len_wrapped(input, wrap),
4271 None => self.engine.decoded_len(input),
4272 }
4273 }
4274
4275 pub fn validate_result(&self, input: &[u8]) -> Result<(), DecodeError> {
4277 match self.wrap {
4278 Some(wrap) => self.engine.validate_wrapped_result(input, wrap),
4279 None => self.engine.validate_result(input),
4280 }
4281 }
4282
4283 #[must_use]
4285 pub fn validate(&self, input: &[u8]) -> bool {
4286 self.validate_result(input).is_ok()
4287 }
4288
4289 pub fn encode_slice(&self, input: &[u8], output: &mut [u8]) -> Result<usize, EncodeError> {
4291 match self.wrap {
4292 Some(wrap) => self.engine.encode_slice_wrapped(input, output, wrap),
4293 None => self.engine.encode_slice(input, output),
4294 }
4295 }
4296
4297 pub fn encode_slice_clear_tail(
4300 &self,
4301 input: &[u8],
4302 output: &mut [u8],
4303 ) -> Result<usize, EncodeError> {
4304 match self.wrap {
4305 Some(wrap) => self
4306 .engine
4307 .encode_slice_wrapped_clear_tail(input, output, wrap),
4308 None => self.engine.encode_slice_clear_tail(input, output),
4309 }
4310 }
4311
4312 pub fn encode_buffer<const CAP: usize>(
4318 &self,
4319 input: &[u8],
4320 ) -> Result<EncodedBuffer<CAP>, EncodeError> {
4321 let mut output = EncodedBuffer::new();
4322 let written = match self.encode_slice_clear_tail(input, &mut output.bytes) {
4323 Ok(written) => written,
4324 Err(err) => {
4325 output.clear();
4326 return Err(err);
4327 }
4328 };
4329 output.len = written;
4330 Ok(output)
4331 }
4332
4333 #[must_use = "handle decode errors; use crate::ct for secret-bearing payloads"]
4346 pub fn decode_slice(&self, input: &[u8], output: &mut [u8]) -> Result<usize, DecodeError> {
4347 match self.wrap {
4348 Some(wrap) => self.engine.decode_slice_wrapped(input, output, wrap),
4349 None => self.engine.decode_slice(input, output),
4350 }
4351 }
4352
4353 pub fn decode_slice_clear_tail(
4356 &self,
4357 input: &[u8],
4358 output: &mut [u8],
4359 ) -> Result<usize, DecodeError> {
4360 match self.wrap {
4361 Some(wrap) => self
4362 .engine
4363 .decode_slice_wrapped_clear_tail(input, output, wrap),
4364 None => self.engine.decode_slice_clear_tail(input, output),
4365 }
4366 }
4367
4368 pub fn decode_buffer<const CAP: usize>(
4374 &self,
4375 input: &[u8],
4376 ) -> Result<DecodedBuffer<CAP>, DecodeError> {
4377 let mut output = DecodedBuffer::new();
4378 let written = match self.decode_slice_clear_tail(input, &mut output.bytes) {
4379 Ok(written) => written,
4380 Err(err) => {
4381 output.clear();
4382 return Err(err);
4383 }
4384 };
4385 output.len = written;
4386 Ok(output)
4387 }
4388
4389 pub fn decode_in_place<'a>(&self, buffer: &'a mut [u8]) -> Result<&'a mut [u8], DecodeError> {
4418 match self.wrap {
4419 Some(wrap) => self.engine.decode_in_place_wrapped(buffer, wrap),
4420 None => self.engine.decode_in_place(buffer),
4421 }
4422 }
4423
4424 pub fn decode_in_place_clear_tail<'a>(
4443 &self,
4444 buffer: &'a mut [u8],
4445 ) -> Result<&'a mut [u8], DecodeError> {
4446 match self.wrap {
4447 Some(wrap) => self.engine.decode_in_place_wrapped_clear_tail(buffer, wrap),
4448 None => self.engine.decode_in_place_clear_tail(buffer),
4449 }
4450 }
4451
4452 #[cfg(feature = "alloc")]
4454 #[must_use = "for secret-bearing payloads use encode_secret, which returns a redacted buffer with drop-time cleanup"]
4455 pub fn encode_vec(&self, input: &[u8]) -> Result<alloc::vec::Vec<u8>, EncodeError> {
4456 match self.wrap {
4457 Some(wrap) => self.engine.encode_wrapped_vec(input, wrap),
4458 None => self.engine.encode_vec(input),
4459 }
4460 }
4461
4462 #[cfg(feature = "alloc")]
4464 pub fn encode_secret(&self, input: &[u8]) -> Result<SecretBuffer, EncodeError> {
4465 self.encode_vec(input).map(SecretBuffer::from_vec)
4466 }
4467
4468 #[cfg(feature = "alloc")]
4470 pub fn encode_string(&self, input: &[u8]) -> Result<alloc::string::String, EncodeError> {
4471 match self.wrap {
4472 Some(wrap) => self.engine.encode_wrapped_string(input, wrap),
4473 None => self.engine.encode_string(input),
4474 }
4475 }
4476
4477 #[cfg(feature = "alloc")]
4479 #[must_use = "for secret-bearing payloads use decode_secret, which returns a redacted buffer with drop-time cleanup"]
4480 pub fn decode_vec(&self, input: &[u8]) -> Result<alloc::vec::Vec<u8>, DecodeError> {
4481 match self.wrap {
4482 Some(wrap) => self.engine.decode_wrapped_vec(input, wrap),
4483 None => self.engine.decode_vec(input),
4484 }
4485 }
4486
4487 #[cfg(feature = "alloc")]
4489 pub fn decode_secret(&self, input: &[u8]) -> Result<SecretBuffer, DecodeError> {
4490 self.decode_vec(input).map(SecretBuffer::from_vec)
4491 }
4492}
4493
4494impl<A, const PAD: bool> Default for Profile<A, PAD>
4495where
4496 A: Alphabet,
4497{
4498 fn default() -> Self {
4499 Self::new(Engine::new(), None)
4500 }
4501}
4502
4503impl<A, const PAD: bool> core::fmt::Display for Profile<A, PAD>
4504where
4505 A: Alphabet,
4506{
4507 fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
4508 match self.wrap {
4509 Some(wrap) => write!(formatter, "padded={PAD} wrap={wrap}"),
4510 None => write!(formatter, "padded={PAD} wrap=none"),
4511 }
4512 }
4513}
4514
4515impl<A, const PAD: bool> From<Engine<A, PAD>> for Profile<A, PAD>
4516where
4517 A: Alphabet,
4518{
4519 fn from(engine: Engine<A, PAD>) -> Self {
4520 Self::new(engine, None)
4521 }
4522}
4523
4524#[doc(alias = "ct")]
4530#[doc(alias = "constant_time")]
4531#[doc(alias = "sensitive")]
4532pub const MIME: Profile<Standard, true> = Profile::new(STANDARD, Some(LineWrap::MIME));
4533
4534#[doc(alias = "ct")]
4540#[doc(alias = "constant_time")]
4541#[doc(alias = "sensitive")]
4542pub const PEM: Profile<Standard, true> = Profile::new(STANDARD, Some(LineWrap::PEM));
4543
4544#[doc(alias = "ct")]
4550#[doc(alias = "constant_time")]
4551#[doc(alias = "sensitive")]
4552pub const PEM_CRLF: Profile<Standard, true> = Profile::new(STANDARD, Some(LineWrap::PEM_CRLF));
4553
4554#[doc(alias = "ct")]
4562#[doc(alias = "constant_time")]
4563#[doc(alias = "sensitive")]
4564pub const BCRYPT: Profile<Bcrypt, false> = Profile::new(BCRYPT_NO_PAD, None);
4565
4566#[doc(alias = "ct")]
4574#[doc(alias = "constant_time")]
4575#[doc(alias = "sensitive")]
4576pub const CRYPT: Profile<Crypt, false> = Profile::new(CRYPT_NO_PAD, None);
4577
4578pub const fn encoded_len(input_len: usize, padded: bool) -> Result<usize, EncodeError> {
4593 match checked_encoded_len(input_len, padded) {
4594 Some(len) => Ok(len),
4595 None => Err(EncodeError::LengthOverflow),
4596 }
4597}
4598
4599pub const fn wrapped_encoded_len(
4613 input_len: usize,
4614 padded: bool,
4615 wrap: LineWrap,
4616) -> Result<usize, EncodeError> {
4617 if wrap.line_len == 0 {
4618 return Err(EncodeError::InvalidLineWrap { line_len: 0 });
4619 }
4620
4621 let Some(encoded) = checked_encoded_len(input_len, padded) else {
4622 return Err(EncodeError::LengthOverflow);
4623 };
4624 if encoded == 0 {
4625 return Ok(0);
4626 }
4627
4628 let breaks = (encoded - 1) / wrap.line_len;
4629 let Some(line_ending_bytes) = breaks.checked_mul(wrap.line_ending.byte_len()) else {
4630 return Err(EncodeError::LengthOverflow);
4631 };
4632 match encoded.checked_add(line_ending_bytes) {
4633 Some(len) => Ok(len),
4634 None => Err(EncodeError::LengthOverflow),
4635 }
4636}
4637
4638#[must_use]
4654pub const fn checked_wrapped_encoded_len(
4655 input_len: usize,
4656 padded: bool,
4657 wrap: LineWrap,
4658) -> Option<usize> {
4659 if wrap.line_len == 0 {
4660 return None;
4661 }
4662
4663 let Some(encoded) = checked_encoded_len(input_len, padded) else {
4664 return None;
4665 };
4666 if encoded == 0 {
4667 return Some(0);
4668 }
4669
4670 let breaks = (encoded - 1) / wrap.line_len;
4671 let Some(line_ending_bytes) = breaks.checked_mul(wrap.line_ending.byte_len()) else {
4672 return None;
4673 };
4674 encoded.checked_add(line_ending_bytes)
4675}
4676
4677#[must_use]
4688pub const fn checked_encoded_len(input_len: usize, padded: bool) -> Option<usize> {
4689 let groups = input_len / 3;
4690 if groups > usize::MAX / 4 {
4691 return None;
4692 }
4693 let full = groups * 4;
4694 let rem = input_len % 3;
4695 if rem == 0 {
4696 Some(full)
4697 } else if padded {
4698 full.checked_add(4)
4699 } else {
4700 full.checked_add(rem + 1)
4701 }
4702}
4703
4704#[must_use]
4722pub fn constant_time_eq_fixed_width<const N: usize>(left: &[u8; N], right: &[u8; N]) -> bool {
4723 constant_time_eq_fixed_width_array(left, right)
4724}
4725
4726#[must_use]
4737pub const fn decoded_capacity(encoded_len: usize) -> usize {
4738 let rem = encoded_len % 4;
4739 encoded_len / 4 * 3
4740 + if rem == 2 {
4741 1
4742 } else if rem == 3 {
4743 2
4744 } else {
4745 0
4746 }
4747}
4748
4749pub fn decoded_len(input: &[u8], padded: bool) -> Result<usize, DecodeError> {
4763 if padded {
4764 decoded_len_padded(input)
4765 } else {
4766 decoded_len_unpadded(input)
4767 }
4768}
4769
4770#[macro_export]
4807macro_rules! define_alphabet {
4808 ($(#[$meta:meta])* $vis:vis struct $name:ident = $encode:expr;) => {
4809 $(#[$meta])*
4810 #[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
4811 $vis struct $name;
4812
4813 impl $crate::Alphabet for $name {
4814 const ENCODE: [u8; 64] = *$encode;
4815
4816 #[inline]
4817 fn decode(byte: u8) -> Option<u8> {
4818 $crate::decode_alphabet_byte(byte, &Self::ENCODE)
4819 }
4820 }
4821
4822 const _: [(); 1] = [(); match $crate::validate_alphabet(
4823 &<$name as $crate::Alphabet>::ENCODE,
4824 ) {
4825 Ok(()) => 1,
4826 Err(_) => 0,
4827 }];
4828 };
4829}
4830
4831pub const fn validate_alphabet(encode: &[u8; 64]) -> Result<(), AlphabetError> {
4844 let mut index = 0;
4845 while index < encode.len() {
4846 let byte = encode[index];
4847 if !is_visible_ascii(byte) {
4848 return Err(AlphabetError::InvalidByte { index, byte });
4849 }
4850 if byte == b'=' {
4851 return Err(AlphabetError::PaddingByte { index });
4852 }
4853
4854 let mut duplicate = index + 1;
4855 while duplicate < encode.len() {
4856 if encode[duplicate] == byte {
4857 return Err(AlphabetError::DuplicateByte {
4858 first: index,
4859 second: duplicate,
4860 byte,
4861 });
4862 }
4863 duplicate += 1;
4864 }
4865
4866 index += 1;
4867 }
4868
4869 Ok(())
4870}
4871
4872#[must_use]
4900pub const fn decode_alphabet_byte(byte: u8, encode: &[u8; 64]) -> Option<u8> {
4901 let mut index = 0;
4902 let mut candidate = 0;
4903 let mut decoded = 0;
4904 let mut valid = 0;
4905 while index < encode.len() {
4906 let matches = ct_mask_eq_u8(byte, encode[index]);
4907 decoded |= candidate & matches;
4908 valid |= matches;
4909 index += 1;
4910 candidate += 1;
4911 }
4912
4913 if valid == 0 { None } else { Some(decoded) }
4914}
4915
4916pub trait Alphabet {
4933 const ENCODE: [u8; 64];
4935
4936 #[must_use]
4947 fn encode(value: u8) -> u8 {
4948 encode_alphabet_value(value, &Self::ENCODE)
4949 }
4950
4951 fn decode(byte: u8) -> Option<u8>;
4958}
4959
4960const fn is_visible_ascii(byte: u8) -> bool {
4961 byte >= 0x21 && byte <= 0x7e
4962}
4963
4964#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
4966pub struct Standard;
4967
4968impl Alphabet for Standard {
4969 const ENCODE: [u8; 64] = *b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
4970
4971 #[inline]
4972 fn encode(value: u8) -> u8 {
4973 encode_ascii_base64(value, Self::ENCODE[62], Self::ENCODE[63])
4974 }
4975
4976 #[inline]
4977 fn decode(byte: u8) -> Option<u8> {
4978 decode_ascii_base64(byte, Self::ENCODE[62], Self::ENCODE[63])
4979 }
4980}
4981
4982#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
4984pub struct UrlSafe;
4985
4986impl Alphabet for UrlSafe {
4987 const ENCODE: [u8; 64] = *b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
4988
4989 #[inline]
4990 fn encode(value: u8) -> u8 {
4991 encode_ascii_base64(value, Self::ENCODE[62], Self::ENCODE[63])
4992 }
4993
4994 #[inline]
4995 fn decode(byte: u8) -> Option<u8> {
4996 decode_ascii_base64(byte, Self::ENCODE[62], Self::ENCODE[63])
4997 }
4998}
4999
5000#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
5006pub struct Bcrypt;
5007
5008impl Alphabet for Bcrypt {
5009 const ENCODE: [u8; 64] = *b"./ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
5010
5011 #[inline]
5012 fn decode(byte: u8) -> Option<u8> {
5013 decode_alphabet_byte(byte, &Self::ENCODE)
5014 }
5015}
5016
5017#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
5022pub struct Crypt;
5023
5024impl Alphabet for Crypt {
5025 const ENCODE: [u8; 64] = *b"./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
5026
5027 #[inline]
5028 fn decode(byte: u8) -> Option<u8> {
5029 decode_alphabet_byte(byte, &Self::ENCODE)
5030 }
5031}
5032
5033#[inline]
5034const fn encode_base64_value<A: Alphabet>(value: u8) -> u8 {
5035 encode_alphabet_value(value, &A::ENCODE)
5036}
5037
5038#[inline]
5039fn encode_base64_value_runtime<A: Alphabet>(value: u8) -> u8 {
5040 A::encode(value)
5041}
5042
5043#[inline]
5044const fn encode_alphabet_value(value: u8, encode: &[u8; 64]) -> u8 {
5045 let mut output = 0;
5046 let mut index = 0;
5047 let mut candidate = 0;
5048 while index < encode.len() {
5049 output |= encode[index] & ct_mask_eq_u8(value, candidate);
5050 index += 1;
5051 candidate += 1;
5052 }
5053 output
5054}
5055
5056#[inline]
5057const fn encode_ascii_base64(value: u8, value_62_byte: u8, value_63_byte: u8) -> u8 {
5058 let upper = ct_mask_lt_u8(value, 26);
5059 let lower = ct_mask_lt_u8(value.wrapping_sub(26), 26);
5060 let digit = ct_mask_lt_u8(value.wrapping_sub(52), 10);
5061 let value_62 = ct_mask_eq_u8(value, 0x3e);
5062 let value_63 = ct_mask_eq_u8(value, 0x3f);
5063
5064 (value.wrapping_add(b'A') & upper)
5065 | (value.wrapping_sub(26).wrapping_add(b'a') & lower)
5066 | (value.wrapping_sub(52).wrapping_add(b'0') & digit)
5067 | (value_62_byte & value_62)
5068 | (value_63_byte & value_63)
5069}
5070
5071#[inline]
5072fn decode_ascii_base64(byte: u8, value_62_byte: u8, value_63_byte: u8) -> Option<u8> {
5073 let upper = ct_mask_lt_u8(byte.wrapping_sub(b'A'), 26);
5074 let lower = ct_mask_lt_u8(byte.wrapping_sub(b'a'), 26);
5075 let digit = ct_mask_lt_u8(byte.wrapping_sub(b'0'), 10);
5076 let value_62 = ct_mask_eq_u8(byte, value_62_byte);
5077 let value_63 = ct_mask_eq_u8(byte, value_63_byte);
5078 let valid = upper | lower | digit | value_62 | value_63;
5079
5080 let decoded = (byte.wrapping_sub(b'A') & upper)
5081 | (byte.wrapping_sub(b'a').wrapping_add(26) & lower)
5082 | (byte.wrapping_sub(b'0').wrapping_add(52) & digit)
5083 | (0x3e & value_62)
5084 | (0x3f & value_63);
5085
5086 if valid == 0 { None } else { Some(decoded) }
5087}
5088
5089#[inline]
5090const fn ct_mask_bit(bit: u8) -> u8 {
5091 0u8.wrapping_sub(bit & 1)
5092}
5093
5094#[inline]
5095const fn ct_mask_nonzero_u8(value: u8) -> u8 {
5096 let wide = value as u16;
5097 let negative = 0u16.wrapping_sub(wide);
5098 let nonzero = ((wide | negative) >> 8) as u8;
5099 ct_mask_bit(nonzero)
5100}
5101
5102#[inline]
5103const fn ct_mask_eq_u8(left: u8, right: u8) -> u8 {
5104 !ct_mask_nonzero_u8(left ^ right)
5105}
5106
5107#[inline]
5108const fn ct_mask_lt_u8(left: u8, right: u8) -> u8 {
5109 let diff = (left as u16).wrapping_sub(right as u16);
5110 ct_mask_bit((diff >> 8) as u8)
5111}
5112
5113#[inline(never)]
5114fn constant_time_eq_public_len(left: &[u8], right: &[u8]) -> bool {
5115 if left.len() != right.len() {
5116 return false;
5117 }
5118
5119 constant_time_eq_same_len(left, right)
5120}
5121
5122#[inline(never)]
5123fn constant_time_eq_fixed_width_array<const N: usize>(left: &[u8; N], right: &[u8; N]) -> bool {
5124 constant_time_eq_same_len(left, right)
5125}
5126
5127#[inline(never)]
5128#[allow(unsafe_code)]
5129fn constant_time_eq_same_len(left: &[u8], right: &[u8]) -> bool {
5130 let mut diff = 0u8;
5131 for (left, right) in left.iter().zip(right) {
5132 diff = core::hint::black_box(
5133 core::hint::black_box(diff) | core::hint::black_box(*left ^ *right),
5134 );
5135 diff = unsafe { core::ptr::read_volatile(&raw const diff) };
5139 }
5140 ct_error_gate_barrier(diff, 0);
5141 let result = unsafe { core::ptr::read_volatile(&raw const diff) };
5145 result == 0
5146}
5147
5148#[cfg(feature = "alloc")]
5149#[allow(unsafe_code)]
5150fn string_from_validated_secret_bytes(bytes: alloc::vec::Vec<u8>) -> alloc::string::String {
5151 unsafe { alloc::string::String::from_utf8_unchecked(bytes) }
5156}
5157
5158mod backend {
5159 use super::{
5160 Alphabet, DecodeError, EncodeError, checked_encoded_len, decode_padded, decode_unpadded,
5161 encode_base64_value_runtime,
5162 };
5163
5164 pub(super) fn encode_slice<A, const PAD: bool>(
5165 input: &[u8],
5166 output: &mut [u8],
5167 ) -> Result<usize, EncodeError>
5168 where
5169 A: Alphabet,
5170 {
5171 #[cfg(feature = "simd")]
5172 match super::simd::active_backend() {
5173 super::simd::ActiveBackend::Scalar => {}
5174 }
5175
5176 scalar_encode_slice::<A, PAD>(input, output)
5177 }
5178
5179 pub(super) fn decode_slice<A, const PAD: bool>(
5180 input: &[u8],
5181 output: &mut [u8],
5182 ) -> Result<usize, DecodeError>
5183 where
5184 A: Alphabet,
5185 {
5186 #[cfg(feature = "simd")]
5187 match super::simd::active_backend() {
5188 super::simd::ActiveBackend::Scalar => {}
5189 }
5190
5191 scalar_decode_slice::<A, PAD>(input, output)
5192 }
5193
5194 #[cfg(test)]
5195 pub(super) fn scalar_reference_encode_slice<A, const PAD: bool>(
5196 input: &[u8],
5197 output: &mut [u8],
5198 ) -> Result<usize, EncodeError>
5199 where
5200 A: Alphabet,
5201 {
5202 scalar_encode_slice::<A, PAD>(input, output)
5203 }
5204
5205 #[cfg(test)]
5206 pub(super) fn scalar_reference_decode_slice<A, const PAD: bool>(
5207 input: &[u8],
5208 output: &mut [u8],
5209 ) -> Result<usize, DecodeError>
5210 where
5211 A: Alphabet,
5212 {
5213 scalar_decode_slice::<A, PAD>(input, output)
5214 }
5215
5216 fn scalar_encode_slice<A, const PAD: bool>(
5217 input: &[u8],
5218 output: &mut [u8],
5219 ) -> Result<usize, EncodeError>
5220 where
5221 A: Alphabet,
5222 {
5223 let required = checked_encoded_len(input.len(), PAD).ok_or(EncodeError::LengthOverflow)?;
5224 if output.len() < required {
5225 return Err(EncodeError::OutputTooSmall {
5226 required,
5227 available: output.len(),
5228 });
5229 }
5230
5231 let mut read = 0;
5232 let mut write = 0;
5233 while read + 3 <= input.len() {
5234 let b0 = input[read];
5235 let b1 = input[read + 1];
5236 let b2 = input[read + 2];
5237
5238 output[write] = encode_base64_value_runtime::<A>(b0 >> 2);
5239 output[write + 1] =
5240 encode_base64_value_runtime::<A>(((b0 & 0b0000_0011) << 4) | (b1 >> 4));
5241 output[write + 2] =
5242 encode_base64_value_runtime::<A>(((b1 & 0b0000_1111) << 2) | (b2 >> 6));
5243 output[write + 3] = encode_base64_value_runtime::<A>(b2 & 0b0011_1111);
5244
5245 read += 3;
5246 write += 4;
5247 }
5248
5249 match input.len() - read {
5250 0 => {}
5251 1 => {
5252 let b0 = input[read];
5253 output[write] = encode_base64_value_runtime::<A>(b0 >> 2);
5254 output[write + 1] = encode_base64_value_runtime::<A>((b0 & 0b0000_0011) << 4);
5255 write += 2;
5256 if PAD {
5257 output[write] = b'=';
5258 output[write + 1] = b'=';
5259 write += 2;
5260 }
5261 }
5262 2 => {
5263 let b0 = input[read];
5264 let b1 = input[read + 1];
5265 output[write] = encode_base64_value_runtime::<A>(b0 >> 2);
5266 output[write + 1] =
5267 encode_base64_value_runtime::<A>(((b0 & 0b0000_0011) << 4) | (b1 >> 4));
5268 output[write + 2] = encode_base64_value_runtime::<A>((b1 & 0b0000_1111) << 2);
5269 write += 3;
5270 if PAD {
5271 output[write] = b'=';
5272 write += 1;
5273 }
5274 }
5275 _ => unreachable!(),
5276 }
5277
5278 Ok(write)
5279 }
5280
5281 fn scalar_decode_slice<A, const PAD: bool>(
5282 input: &[u8],
5283 output: &mut [u8],
5284 ) -> Result<usize, DecodeError>
5285 where
5286 A: Alphabet,
5287 {
5288 if input.is_empty() {
5289 return Ok(0);
5290 }
5291
5292 if PAD {
5293 decode_padded::<A>(input, output)
5294 } else {
5295 decode_unpadded::<A>(input, output)
5296 }
5297 }
5298}
5299
5300pub struct Engine<A, const PAD: bool> {
5302 alphabet: core::marker::PhantomData<A>,
5303}
5304
5305impl<A, const PAD: bool> Clone for Engine<A, PAD> {
5306 fn clone(&self) -> Self {
5307 *self
5308 }
5309}
5310
5311impl<A, const PAD: bool> Copy for Engine<A, PAD> {}
5312
5313impl<A, const PAD: bool> core::fmt::Debug for Engine<A, PAD> {
5314 fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
5315 formatter
5316 .debug_struct("Engine")
5317 .field("padded", &PAD)
5318 .finish()
5319 }
5320}
5321
5322impl<A, const PAD: bool> core::fmt::Display for Engine<A, PAD> {
5323 fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
5324 write!(formatter, "padded={PAD}")
5325 }
5326}
5327
5328impl<A, const PAD: bool> Default for Engine<A, PAD> {
5329 fn default() -> Self {
5330 Self {
5331 alphabet: core::marker::PhantomData,
5332 }
5333 }
5334}
5335
5336impl<A, const PAD: bool> Eq for Engine<A, PAD> {}
5337
5338impl<A, const PAD: bool> PartialEq for Engine<A, PAD> {
5339 fn eq(&self, _other: &Self) -> bool {
5340 true
5341 }
5342}
5343
5344impl<A, const PAD: bool> Engine<A, PAD>
5345where
5346 A: Alphabet,
5347{
5348 #[must_use]
5350 pub const fn new() -> Self {
5351 Self {
5352 alphabet: core::marker::PhantomData,
5353 }
5354 }
5355
5356 #[must_use]
5358 pub const fn is_padded(&self) -> bool {
5359 PAD
5360 }
5361
5362 #[must_use]
5367 pub const fn profile(&self) -> Profile<A, PAD> {
5368 Profile::new(*self, None)
5369 }
5370
5371 #[must_use]
5377 pub const fn ct_decoder(&self) -> ct::CtEngine<A, PAD> {
5378 ct::CtEngine::new()
5379 }
5380
5381 #[cfg(feature = "stream")]
5395 #[must_use]
5396 pub fn encoder_writer<W>(&self, inner: W) -> stream::Encoder<W, A, PAD> {
5397 stream::Encoder::new(inner, *self)
5398 }
5399
5400 #[cfg(feature = "stream")]
5420 #[must_use]
5421 pub fn decoder_writer<W>(&self, inner: W) -> stream::Decoder<W, A, PAD> {
5422 stream::Decoder::new(inner, *self)
5423 }
5424
5425 #[cfg(feature = "stream")]
5440 #[must_use]
5441 pub fn encoder_reader<R>(&self, inner: R) -> stream::EncoderReader<R, A, PAD> {
5442 stream::EncoderReader::new(inner, *self)
5443 }
5444
5445 #[cfg(feature = "stream")]
5466 #[must_use]
5467 pub fn decoder_reader<R>(&self, inner: R) -> stream::DecoderReader<R, A, PAD> {
5468 stream::DecoderReader::new(inner, *self)
5469 }
5470
5471 pub const fn encoded_len(&self, input_len: usize) -> Result<usize, EncodeError> {
5473 encoded_len(input_len, PAD)
5474 }
5475
5476 #[must_use]
5478 pub const fn checked_encoded_len(&self, input_len: usize) -> Option<usize> {
5479 checked_encoded_len(input_len, PAD)
5480 }
5481
5482 pub const fn wrapped_encoded_len(
5487 &self,
5488 input_len: usize,
5489 wrap: LineWrap,
5490 ) -> Result<usize, EncodeError> {
5491 wrapped_encoded_len(input_len, PAD, wrap)
5492 }
5493
5494 #[must_use]
5497 pub const fn checked_wrapped_encoded_len(
5498 &self,
5499 input_len: usize,
5500 wrap: LineWrap,
5501 ) -> Option<usize> {
5502 checked_wrapped_encoded_len(input_len, PAD, wrap)
5503 }
5504
5505 pub fn decoded_len(&self, input: &[u8]) -> Result<usize, DecodeError> {
5510 decoded_len(input, PAD)
5511 }
5512
5513 pub fn decoded_len_legacy(&self, input: &[u8]) -> Result<usize, DecodeError> {
5519 validate_legacy_decode::<A, PAD>(input)
5520 }
5521
5522 pub fn decoded_len_wrapped(&self, input: &[u8], wrap: LineWrap) -> Result<usize, DecodeError> {
5529 validate_wrapped_decode::<A, PAD>(input, wrap)
5530 }
5531
5532 pub fn validate_result(&self, input: &[u8]) -> Result<(), DecodeError> {
5550 validate_decode::<A, PAD>(input).map(|_| ())
5551 }
5552
5553 #[must_use]
5569 pub fn validate(&self, input: &[u8]) -> bool {
5570 self.validate_result(input).is_ok()
5571 }
5572
5573 pub fn validate_legacy_result(&self, input: &[u8]) -> Result<(), DecodeError> {
5588 validate_legacy_decode::<A, PAD>(input).map(|_| ())
5589 }
5590
5591 #[must_use]
5605 pub fn validate_legacy(&self, input: &[u8]) -> bool {
5606 self.validate_legacy_result(input).is_ok()
5607 }
5608
5609 pub fn validate_wrapped_result(&self, input: &[u8], wrap: LineWrap) -> Result<(), DecodeError> {
5625 validate_wrapped_decode::<A, PAD>(input, wrap).map(|_| ())
5626 }
5627
5628 #[must_use]
5642 pub fn validate_wrapped(&self, input: &[u8], wrap: LineWrap) -> bool {
5643 self.validate_wrapped_result(input, wrap).is_ok()
5644 }
5645
5646 #[must_use]
5678 pub const fn encode_array<const INPUT_LEN: usize, const OUTPUT_LEN: usize>(
5679 &self,
5680 input: &[u8; INPUT_LEN],
5681 ) -> [u8; OUTPUT_LEN] {
5682 let Some(required) = checked_encoded_len(INPUT_LEN, PAD) else {
5683 panic!("encoded base64 length overflows usize");
5684 };
5685 assert!(
5686 required == OUTPUT_LEN,
5687 "base64 output array has incorrect length"
5688 );
5689
5690 let mut output = [0u8; OUTPUT_LEN];
5691 let mut read = 0;
5692 let mut write = 0;
5693 while INPUT_LEN - read >= 3 {
5694 let b0 = input[read];
5695 let b1 = input[read + 1];
5696 let b2 = input[read + 2];
5697
5698 output[write] = encode_base64_value::<A>(b0 >> 2);
5699 output[write + 1] = encode_base64_value::<A>(((b0 & 0b0000_0011) << 4) | (b1 >> 4));
5700 output[write + 2] = encode_base64_value::<A>(((b1 & 0b0000_1111) << 2) | (b2 >> 6));
5701 output[write + 3] = encode_base64_value::<A>(b2 & 0b0011_1111);
5702
5703 read += 3;
5704 write += 4;
5705 }
5706
5707 match INPUT_LEN - read {
5708 0 => {}
5709 1 => {
5710 let b0 = input[read];
5711 output[write] = encode_base64_value::<A>(b0 >> 2);
5712 output[write + 1] = encode_base64_value::<A>((b0 & 0b0000_0011) << 4);
5713 write += 2;
5714 if PAD {
5715 output[write] = b'=';
5716 output[write + 1] = b'=';
5717 }
5718 }
5719 2 => {
5720 let b0 = input[read];
5721 let b1 = input[read + 1];
5722 output[write] = encode_base64_value::<A>(b0 >> 2);
5723 output[write + 1] = encode_base64_value::<A>(((b0 & 0b0000_0011) << 4) | (b1 >> 4));
5724 output[write + 2] = encode_base64_value::<A>((b1 & 0b0000_1111) << 2);
5725 if PAD {
5726 output[write + 3] = b'=';
5727 }
5728 }
5729 _ => unreachable!(),
5730 }
5731
5732 output
5733 }
5734
5735 pub fn encode_slice(&self, input: &[u8], output: &mut [u8]) -> Result<usize, EncodeError> {
5737 backend::encode_slice::<A, PAD>(input, output)
5738 }
5739
5740 pub fn encode_slice_wrapped(
5759 &self,
5760 input: &[u8],
5761 output: &mut [u8],
5762 wrap: LineWrap,
5763 ) -> Result<usize, EncodeError> {
5764 let required = self.wrapped_encoded_len(input.len(), wrap)?;
5765 if output.len() < required {
5766 return Err(EncodeError::OutputTooSmall {
5767 required,
5768 available: output.len(),
5769 });
5770 }
5771
5772 let encoded_len =
5773 checked_encoded_len(input.len(), PAD).ok_or(EncodeError::LengthOverflow)?;
5774 if encoded_len == 0 {
5775 return Ok(0);
5776 }
5777
5778 let combined_required = match required.checked_add(encoded_len) {
5781 Some(len) => len,
5782 None => usize::MAX,
5783 };
5784 if output.len() < combined_required {
5785 let mut scratch = [0u8; 1024];
5786 let mut input_offset = 0;
5787 let mut output_offset = 0;
5788 let mut column = 0;
5789
5790 while input_offset < input.len() {
5791 let remaining = input.len() - input_offset;
5792 let mut take = remaining.min(768);
5793 if remaining > take {
5794 take -= take % 3;
5795 }
5796 if take == 0 {
5797 take = remaining;
5798 }
5799
5800 let encoded = match self
5801 .encode_slice(&input[input_offset..input_offset + take], &mut scratch)
5802 {
5803 Ok(encoded) => encoded,
5804 Err(err) => {
5805 wipe_bytes(&mut scratch);
5806 return Err(err);
5807 }
5808 };
5809 if let Err(err) = write_wrapped_bytes(
5810 &scratch[..encoded],
5811 output,
5812 &mut output_offset,
5813 &mut column,
5814 wrap,
5815 ) {
5816 wipe_bytes(&mut scratch);
5817 return Err(err);
5818 }
5819 wipe_bytes(&mut scratch[..encoded]);
5820 input_offset += take;
5821 }
5822
5823 Ok(output_offset)
5824 } else {
5825 let encoded =
5826 self.encode_slice(input, &mut output[required..required + encoded_len])?;
5827 let mut output_offset = 0;
5828 let mut column = 0;
5829 let mut read = required;
5830 while read < required + encoded {
5831 let byte = output[read];
5832 write_wrapped_byte(byte, output, &mut output_offset, &mut column, wrap)?;
5833 read += 1;
5834 }
5835 wipe_bytes(&mut output[required..required + encoded]);
5836 Ok(output_offset)
5837 }
5838 }
5839
5840 pub fn encode_slice_wrapped_clear_tail(
5846 &self,
5847 input: &[u8],
5848 output: &mut [u8],
5849 wrap: LineWrap,
5850 ) -> Result<usize, EncodeError> {
5851 let written = match self.encode_slice_wrapped(input, output, wrap) {
5852 Ok(written) => written,
5853 Err(err) => {
5854 wipe_bytes(output);
5855 return Err(err);
5856 }
5857 };
5858 wipe_tail(output, written);
5859 Ok(written)
5860 }
5861
5862 pub fn encode_wrapped_buffer<const CAP: usize>(
5868 &self,
5869 input: &[u8],
5870 wrap: LineWrap,
5871 ) -> Result<EncodedBuffer<CAP>, EncodeError> {
5872 let mut output = EncodedBuffer::new();
5873 let written = match self.encode_slice_wrapped_clear_tail(input, &mut output.bytes, wrap) {
5874 Ok(written) => written,
5875 Err(err) => {
5876 output.clear();
5877 return Err(err);
5878 }
5879 };
5880 output.len = written;
5881 Ok(output)
5882 }
5883
5884 #[cfg(feature = "alloc")]
5886 #[must_use = "for secret-bearing payloads use encode_wrapped_secret, which returns a redacted buffer with drop-time cleanup"]
5887 pub fn encode_wrapped_vec(
5888 &self,
5889 input: &[u8],
5890 wrap: LineWrap,
5891 ) -> Result<alloc::vec::Vec<u8>, EncodeError> {
5892 let required = self.wrapped_encoded_len(input.len(), wrap)?;
5893 let mut output = alloc::vec![0; required];
5894 let written = self.encode_slice_wrapped(input, &mut output, wrap)?;
5895 output.truncate(written);
5896 Ok(output)
5897 }
5898
5899 #[cfg(feature = "alloc")]
5901 pub fn encode_wrapped_string(
5902 &self,
5903 input: &[u8],
5904 wrap: LineWrap,
5905 ) -> Result<alloc::string::String, EncodeError> {
5906 let output = self.encode_wrapped_vec(input, wrap)?;
5907 match alloc::string::String::from_utf8(output) {
5908 Ok(output) => Ok(output),
5909 Err(_) => unreachable!("base64 encoder produced non-UTF-8 output"),
5910 }
5911 }
5912
5913 #[cfg(feature = "alloc")]
5918 pub fn encode_wrapped_secret(
5919 &self,
5920 input: &[u8],
5921 wrap: LineWrap,
5922 ) -> Result<SecretBuffer, EncodeError> {
5923 self.encode_wrapped_vec(input, wrap)
5924 .map(SecretBuffer::from_vec)
5925 }
5926
5927 pub fn encode_slice_clear_tail(
5947 &self,
5948 input: &[u8],
5949 output: &mut [u8],
5950 ) -> Result<usize, EncodeError> {
5951 let written = match self.encode_slice(input, output) {
5952 Ok(written) => written,
5953 Err(err) => {
5954 wipe_bytes(output);
5955 return Err(err);
5956 }
5957 };
5958 wipe_tail(output, written);
5959 Ok(written)
5960 }
5961
5962 pub fn encode_buffer<const CAP: usize>(
5977 &self,
5978 input: &[u8],
5979 ) -> Result<EncodedBuffer<CAP>, EncodeError> {
5980 let mut output = EncodedBuffer::new();
5981 let written = match self.encode_slice_clear_tail(input, &mut output.bytes) {
5982 Ok(written) => written,
5983 Err(err) => {
5984 output.clear();
5985 return Err(err);
5986 }
5987 };
5988 output.len = written;
5989 Ok(output)
5990 }
5991
5992 #[cfg(feature = "alloc")]
5994 #[must_use = "for secret-bearing payloads use encode_secret, which returns a redacted buffer with drop-time cleanup"]
5995 pub fn encode_vec(&self, input: &[u8]) -> Result<alloc::vec::Vec<u8>, EncodeError> {
5996 let required = checked_encoded_len(input.len(), PAD).ok_or(EncodeError::LengthOverflow)?;
5997 let mut output = alloc::vec![0; required];
5998 let written = self.encode_slice(input, &mut output)?;
5999 output.truncate(written);
6000 Ok(output)
6001 }
6002
6003 #[cfg(feature = "alloc")]
6008 pub fn encode_secret(&self, input: &[u8]) -> Result<SecretBuffer, EncodeError> {
6009 self.encode_vec(input).map(SecretBuffer::from_vec)
6010 }
6011
6012 #[cfg(feature = "alloc")]
6027 pub fn encode_string(&self, input: &[u8]) -> Result<alloc::string::String, EncodeError> {
6028 let output = self.encode_vec(input)?;
6029 match alloc::string::String::from_utf8(output) {
6030 Ok(output) => Ok(output),
6031 Err(_) => unreachable!("base64 encoder produced non-UTF-8 output"),
6032 }
6033 }
6034
6035 pub fn encode_in_place<'a>(
6052 &self,
6053 buffer: &'a mut [u8],
6054 input_len: usize,
6055 ) -> Result<&'a mut [u8], EncodeError> {
6056 if input_len > buffer.len() {
6057 return Err(EncodeError::InputTooLarge {
6058 input_len,
6059 buffer_len: buffer.len(),
6060 });
6061 }
6062
6063 let required = checked_encoded_len(input_len, PAD).ok_or(EncodeError::LengthOverflow)?;
6064 if buffer.len() < required {
6065 return Err(EncodeError::OutputTooSmall {
6066 required,
6067 available: buffer.len(),
6068 });
6069 }
6070
6071 let mut read = input_len;
6072 let mut write = required;
6073
6074 match input_len % 3 {
6075 0 => {}
6076 1 => {
6077 read -= 1;
6078 let b0 = buffer[read];
6079 if PAD {
6080 write -= 4;
6081 buffer[write] = encode_base64_value_runtime::<A>(b0 >> 2);
6082 buffer[write + 1] = encode_base64_value_runtime::<A>((b0 & 0b0000_0011) << 4);
6083 buffer[write + 2] = b'=';
6084 buffer[write + 3] = b'=';
6085 } else {
6086 write -= 2;
6087 buffer[write] = encode_base64_value_runtime::<A>(b0 >> 2);
6088 buffer[write + 1] = encode_base64_value_runtime::<A>((b0 & 0b0000_0011) << 4);
6089 }
6090 }
6091 2 => {
6092 read -= 2;
6093 let b0 = buffer[read];
6094 let b1 = buffer[read + 1];
6095 if PAD {
6096 write -= 4;
6097 buffer[write] = encode_base64_value_runtime::<A>(b0 >> 2);
6098 buffer[write + 1] =
6099 encode_base64_value_runtime::<A>(((b0 & 0b0000_0011) << 4) | (b1 >> 4));
6100 buffer[write + 2] = encode_base64_value_runtime::<A>((b1 & 0b0000_1111) << 2);
6101 buffer[write + 3] = b'=';
6102 } else {
6103 write -= 3;
6104 buffer[write] = encode_base64_value_runtime::<A>(b0 >> 2);
6105 buffer[write + 1] =
6106 encode_base64_value_runtime::<A>(((b0 & 0b0000_0011) << 4) | (b1 >> 4));
6107 buffer[write + 2] = encode_base64_value_runtime::<A>((b1 & 0b0000_1111) << 2);
6108 }
6109 }
6110 _ => unreachable!(),
6111 }
6112
6113 while read > 0 {
6114 read -= 3;
6115 write -= 4;
6116 let b0 = buffer[read];
6117 let b1 = buffer[read + 1];
6118 let b2 = buffer[read + 2];
6119
6120 buffer[write] = encode_base64_value_runtime::<A>(b0 >> 2);
6121 buffer[write + 1] =
6122 encode_base64_value_runtime::<A>(((b0 & 0b0000_0011) << 4) | (b1 >> 4));
6123 buffer[write + 2] =
6124 encode_base64_value_runtime::<A>(((b1 & 0b0000_1111) << 2) | (b2 >> 6));
6125 buffer[write + 3] = encode_base64_value_runtime::<A>(b2 & 0b0011_1111);
6126 }
6127
6128 debug_assert_eq!(write, 0);
6132 Ok(&mut buffer[..required])
6133 }
6134
6135 pub fn encode_in_place_clear_tail<'a>(
6153 &self,
6154 buffer: &'a mut [u8],
6155 input_len: usize,
6156 ) -> Result<&'a mut [u8], EncodeError> {
6157 let len = match self.encode_in_place(buffer, input_len) {
6158 Ok(encoded) => encoded.len(),
6159 Err(err) => {
6160 wipe_bytes(buffer);
6161 return Err(err);
6162 }
6163 };
6164 wipe_tail(buffer, len);
6165 Ok(&mut buffer[..len])
6166 }
6167
6168 #[must_use = "handle decode errors; use crate::ct for secret-bearing payloads"]
6186 pub fn decode_slice(&self, input: &[u8], output: &mut [u8]) -> Result<usize, DecodeError> {
6187 backend::decode_slice::<A, PAD>(input, output)
6188 }
6189
6190 pub fn decode_slice_clear_tail(
6210 &self,
6211 input: &[u8],
6212 output: &mut [u8],
6213 ) -> Result<usize, DecodeError> {
6214 let written = match self.decode_slice(input, output) {
6215 Ok(written) => written,
6216 Err(err) => {
6217 wipe_bytes(output);
6218 return Err(err);
6219 }
6220 };
6221 wipe_tail(output, written);
6222 Ok(written)
6223 }
6224
6225 pub fn decode_buffer<const CAP: usize>(
6240 &self,
6241 input: &[u8],
6242 ) -> Result<DecodedBuffer<CAP>, DecodeError> {
6243 let mut output = DecodedBuffer::new();
6244 let written = match self.decode_slice_clear_tail(input, &mut output.bytes) {
6245 Ok(written) => written,
6246 Err(err) => {
6247 output.clear();
6248 return Err(err);
6249 }
6250 };
6251 output.len = written;
6252 Ok(output)
6253 }
6254
6255 #[must_use = "handle decode errors; use crate::ct for secret-bearing payloads"]
6268 pub fn decode_slice_legacy(
6269 &self,
6270 input: &[u8],
6271 output: &mut [u8],
6272 ) -> Result<usize, DecodeError> {
6273 let required = validate_legacy_decode::<A, PAD>(input)?;
6274 if output.len() < required {
6275 return Err(DecodeError::OutputTooSmall {
6276 required,
6277 available: output.len(),
6278 });
6279 }
6280 decode_legacy_to_slice::<A, PAD>(input, output)
6281 }
6282
6283 pub fn decode_slice_legacy_clear_tail(
6303 &self,
6304 input: &[u8],
6305 output: &mut [u8],
6306 ) -> Result<usize, DecodeError> {
6307 let written = match self.decode_slice_legacy(input, output) {
6308 Ok(written) => written,
6309 Err(err) => {
6310 wipe_bytes(output);
6311 return Err(err);
6312 }
6313 };
6314 wipe_tail(output, written);
6315 Ok(written)
6316 }
6317
6318 pub fn decode_buffer_legacy<const CAP: usize>(
6326 &self,
6327 input: &[u8],
6328 ) -> Result<DecodedBuffer<CAP>, DecodeError> {
6329 let mut output = DecodedBuffer::new();
6330 let written = match self.decode_slice_legacy_clear_tail(input, &mut output.bytes) {
6331 Ok(written) => written,
6332 Err(err) => {
6333 output.clear();
6334 return Err(err);
6335 }
6336 };
6337 output.len = written;
6338 Ok(output)
6339 }
6340
6341 #[must_use = "handle decode errors; use crate::ct for secret-bearing payloads"]
6355 pub fn decode_slice_wrapped(
6356 &self,
6357 input: &[u8],
6358 output: &mut [u8],
6359 wrap: LineWrap,
6360 ) -> Result<usize, DecodeError> {
6361 let required = validate_wrapped_decode::<A, PAD>(input, wrap)?;
6362 if output.len() < required {
6363 return Err(DecodeError::OutputTooSmall {
6364 required,
6365 available: output.len(),
6366 });
6367 }
6368 decode_wrapped_to_slice::<A, PAD>(input, output, wrap)
6369 }
6370
6371 pub fn decode_slice_wrapped_clear_tail(
6377 &self,
6378 input: &[u8],
6379 output: &mut [u8],
6380 wrap: LineWrap,
6381 ) -> Result<usize, DecodeError> {
6382 let written = match self.decode_slice_wrapped(input, output, wrap) {
6383 Ok(written) => written,
6384 Err(err) => {
6385 wipe_bytes(output);
6386 return Err(err);
6387 }
6388 };
6389 wipe_tail(output, written);
6390 Ok(written)
6391 }
6392
6393 pub fn decode_wrapped_buffer<const CAP: usize>(
6402 &self,
6403 input: &[u8],
6404 wrap: LineWrap,
6405 ) -> Result<DecodedBuffer<CAP>, DecodeError> {
6406 let mut output = DecodedBuffer::new();
6407 let written = match self.decode_slice_wrapped_clear_tail(input, &mut output.bytes, wrap) {
6408 Ok(written) => written,
6409 Err(err) => {
6410 output.clear();
6411 return Err(err);
6412 }
6413 };
6414 output.len = written;
6415 Ok(output)
6416 }
6417
6418 #[cfg(feature = "alloc")]
6422 #[must_use = "for secret-bearing payloads use decode_secret, which returns a redacted buffer with drop-time cleanup"]
6423 pub fn decode_vec(&self, input: &[u8]) -> Result<alloc::vec::Vec<u8>, DecodeError> {
6424 let required = validate_decode::<A, PAD>(input)?;
6425 let mut output = alloc::vec![0; required];
6426 let written = match self.decode_slice(input, &mut output) {
6427 Ok(written) => written,
6428 Err(err) => {
6429 wipe_bytes(&mut output);
6430 return Err(err);
6431 }
6432 };
6433 output.truncate(written);
6434 Ok(output)
6435 }
6436
6437 #[cfg(feature = "alloc")]
6442 pub fn decode_secret(&self, input: &[u8]) -> Result<SecretBuffer, DecodeError> {
6443 self.decode_vec(input).map(SecretBuffer::from_vec)
6444 }
6445
6446 #[cfg(feature = "alloc")]
6449 #[must_use = "for secret-bearing payloads use decode_secret_legacy, which returns a redacted buffer with drop-time cleanup"]
6450 pub fn decode_vec_legacy(&self, input: &[u8]) -> Result<alloc::vec::Vec<u8>, DecodeError> {
6451 let required = validate_legacy_decode::<A, PAD>(input)?;
6452 let mut output = alloc::vec![0; required];
6453 let written = match self.decode_slice_legacy(input, &mut output) {
6454 Ok(written) => written,
6455 Err(err) => {
6456 wipe_bytes(&mut output);
6457 return Err(err);
6458 }
6459 };
6460 output.truncate(written);
6461 Ok(output)
6462 }
6463
6464 #[cfg(feature = "alloc")]
6471 pub fn decode_secret_legacy(&self, input: &[u8]) -> Result<SecretBuffer, DecodeError> {
6472 self.decode_vec_legacy(input).map(SecretBuffer::from_vec)
6473 }
6474
6475 #[cfg(feature = "alloc")]
6477 #[must_use = "for secret-bearing payloads use decode_wrapped_secret, which returns a redacted buffer with drop-time cleanup"]
6478 pub fn decode_wrapped_vec(
6479 &self,
6480 input: &[u8],
6481 wrap: LineWrap,
6482 ) -> Result<alloc::vec::Vec<u8>, DecodeError> {
6483 let required = validate_wrapped_decode::<A, PAD>(input, wrap)?;
6484 let mut output = alloc::vec![0; required];
6485 let written = match self.decode_slice_wrapped(input, &mut output, wrap) {
6486 Ok(written) => written,
6487 Err(err) => {
6488 wipe_bytes(&mut output);
6489 return Err(err);
6490 }
6491 };
6492 output.truncate(written);
6493 Ok(output)
6494 }
6495
6496 #[cfg(feature = "alloc")]
6503 pub fn decode_wrapped_secret(
6504 &self,
6505 input: &[u8],
6506 wrap: LineWrap,
6507 ) -> Result<SecretBuffer, DecodeError> {
6508 self.decode_wrapped_vec(input, wrap)
6509 .map(SecretBuffer::from_vec)
6510 }
6511
6512 pub fn decode_in_place_wrapped<'a>(
6536 &self,
6537 buffer: &'a mut [u8],
6538 wrap: LineWrap,
6539 ) -> Result<&'a mut [u8], DecodeError> {
6540 let _required = validate_wrapped_decode::<A, PAD>(buffer, wrap)?;
6541 let compacted = compact_wrapped_input(buffer, wrap)?;
6542 let len = Self::decode_slice_to_start(&mut buffer[..compacted])?;
6543 Ok(&mut buffer[..len])
6544 }
6545
6546 pub fn decode_in_place_wrapped_clear_tail<'a>(
6567 &self,
6568 buffer: &'a mut [u8],
6569 wrap: LineWrap,
6570 ) -> Result<&'a mut [u8], DecodeError> {
6571 if let Err(err) = validate_wrapped_decode::<A, PAD>(buffer, wrap) {
6572 wipe_bytes(buffer);
6573 return Err(err);
6574 }
6575
6576 let compacted = match compact_wrapped_input(buffer, wrap) {
6577 Ok(compacted) => compacted,
6578 Err(err) => {
6579 wipe_bytes(buffer);
6580 return Err(err);
6581 }
6582 };
6583
6584 let len = match Self::decode_slice_to_start(&mut buffer[..compacted]) {
6585 Ok(len) => len,
6586 Err(err) => {
6587 wipe_bytes(buffer);
6588 return Err(err);
6589 }
6590 };
6591 wipe_tail(buffer, len);
6592 Ok(&mut buffer[..len])
6593 }
6594
6595 pub fn decode_in_place<'a>(&self, buffer: &'a mut [u8]) -> Result<&'a mut [u8], DecodeError> {
6620 let len = Self::decode_slice_to_start(buffer)?;
6621 Ok(&mut buffer[..len])
6622 }
6623
6624 pub fn decode_in_place_clear_tail<'a>(
6641 &self,
6642 buffer: &'a mut [u8],
6643 ) -> Result<&'a mut [u8], DecodeError> {
6644 let len = match Self::decode_slice_to_start(buffer) {
6645 Ok(len) => len,
6646 Err(err) => {
6647 wipe_bytes(buffer);
6648 return Err(err);
6649 }
6650 };
6651 wipe_tail(buffer, len);
6652 Ok(&mut buffer[..len])
6653 }
6654
6655 pub fn decode_in_place_legacy<'a>(
6663 &self,
6664 buffer: &'a mut [u8],
6665 ) -> Result<&'a mut [u8], DecodeError> {
6666 let _required = validate_legacy_decode::<A, PAD>(buffer)?;
6667 let mut write = 0;
6668 let mut read = 0;
6669 while read < buffer.len() {
6670 let byte = buffer[read];
6671 if !is_legacy_whitespace(byte) {
6672 buffer[write] = byte;
6673 write += 1;
6674 }
6675 read += 1;
6676 }
6677 let len = Self::decode_slice_to_start(&mut buffer[..write])?;
6678 Ok(&mut buffer[..len])
6679 }
6680
6681 pub fn decode_in_place_legacy_clear_tail<'a>(
6687 &self,
6688 buffer: &'a mut [u8],
6689 ) -> Result<&'a mut [u8], DecodeError> {
6690 if let Err(err) = validate_legacy_decode::<A, PAD>(buffer) {
6691 wipe_bytes(buffer);
6692 return Err(err);
6693 }
6694
6695 let mut write = 0;
6696 let mut read = 0;
6697 while read < buffer.len() {
6698 let byte = buffer[read];
6699 if !is_legacy_whitespace(byte) {
6700 buffer[write] = byte;
6701 write += 1;
6702 }
6703 read += 1;
6704 }
6705
6706 let len = match Self::decode_slice_to_start(&mut buffer[..write]) {
6707 Ok(len) => len,
6708 Err(err) => {
6709 wipe_bytes(buffer);
6710 return Err(err);
6711 }
6712 };
6713 wipe_tail(buffer, len);
6714 Ok(&mut buffer[..len])
6715 }
6716
6717 fn decode_slice_to_start(buffer: &mut [u8]) -> Result<usize, DecodeError> {
6718 let _required = validate_decode::<A, PAD>(buffer)?;
6719 let input_len = buffer.len();
6720 let mut read = 0;
6721 let mut write = 0;
6722 while read + 4 <= input_len {
6723 let chunk = read_quad(buffer, read)?;
6724 let available = buffer.len();
6725 let output_tail = buffer.get_mut(write..).ok_or(DecodeError::OutputTooSmall {
6726 required: write,
6727 available,
6728 })?;
6729 let written = decode_chunk::<A, PAD>(chunk, output_tail)
6730 .map_err(|err| err.with_index_offset(read))?;
6731 read += 4;
6732 write += written;
6733 if written < 3 {
6734 if read != input_len {
6735 return Err(DecodeError::InvalidPadding { index: read - 4 });
6736 }
6737 return Ok(write);
6738 }
6739 }
6740
6741 let rem = input_len - read;
6742 if rem == 0 {
6743 return Ok(write);
6744 }
6745 if PAD {
6746 return Err(DecodeError::InvalidLength);
6747 }
6748 let mut tail = [0u8; 3];
6749 tail[..rem].copy_from_slice(&buffer[read..input_len]);
6750 decode_tail_unpadded::<A>(&tail[..rem], &mut buffer[write..])
6751 .map_err(|err| err.with_index_offset(read))
6752 .map(|n| write + n)
6753 }
6754}
6755
6756fn write_wrapped_bytes(
6757 input: &[u8],
6758 output: &mut [u8],
6759 output_offset: &mut usize,
6760 column: &mut usize,
6761 wrap: LineWrap,
6762) -> Result<(), EncodeError> {
6763 for byte in input {
6764 write_wrapped_byte(*byte, output, output_offset, column, wrap)?;
6765 }
6766 Ok(())
6767}
6768
6769fn write_wrapped_byte(
6770 byte: u8,
6771 output: &mut [u8],
6772 output_offset: &mut usize,
6773 column: &mut usize,
6774 wrap: LineWrap,
6775) -> Result<(), EncodeError> {
6776 if *column == wrap.line_len {
6777 let line_ending = wrap.line_ending.as_bytes();
6778 let mut index = 0;
6779 while index < line_ending.len() {
6780 if *output_offset >= output.len() {
6781 return Err(EncodeError::OutputTooSmall {
6782 required: *output_offset + 1,
6783 available: output.len(),
6784 });
6785 }
6786 output[*output_offset] = line_ending[index];
6787 *output_offset += 1;
6788 index += 1;
6789 }
6790 *column = 0;
6791 }
6792
6793 if *output_offset >= output.len() {
6794 return Err(EncodeError::OutputTooSmall {
6795 required: *output_offset + 1,
6796 available: output.len(),
6797 });
6798 }
6799 output[*output_offset] = byte;
6800 *output_offset += 1;
6801 *column += 1;
6802 Ok(())
6803}
6804
6805#[derive(Clone, Copy, Debug, Eq, PartialEq)]
6807pub enum EncodeError {
6808 LengthOverflow,
6810 InvalidLineWrap {
6812 line_len: usize,
6814 },
6815 InputTooLarge {
6817 input_len: usize,
6819 buffer_len: usize,
6821 },
6822 OutputTooSmall {
6824 required: usize,
6826 available: usize,
6828 },
6829}
6830
6831impl core::fmt::Display for EncodeError {
6832 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
6833 match self {
6834 Self::LengthOverflow => f.write_str("base64 output length overflows usize"),
6835 Self::InvalidLineWrap { line_len } => {
6836 write!(f, "base64 line wrap length {line_len} is invalid")
6837 }
6838 Self::InputTooLarge {
6839 input_len,
6840 buffer_len,
6841 } => write!(
6842 f,
6843 "base64 input length {input_len} exceeds buffer length {buffer_len}"
6844 ),
6845 Self::OutputTooSmall {
6846 required,
6847 available,
6848 } => write!(
6849 f,
6850 "base64 output buffer too small: required {required}, available {available}"
6851 ),
6852 }
6853 }
6854}
6855
6856#[cfg(feature = "std")]
6857impl std::error::Error for EncodeError {}
6858
6859#[derive(Clone, Copy, Debug, Eq, PartialEq)]
6861pub enum AlphabetError {
6862 InvalidByte {
6864 index: usize,
6866 byte: u8,
6868 },
6869 PaddingByte {
6871 index: usize,
6873 },
6874 DuplicateByte {
6876 first: usize,
6878 second: usize,
6880 byte: u8,
6882 },
6883}
6884
6885impl core::fmt::Display for AlphabetError {
6886 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
6887 match self {
6888 Self::InvalidByte { index, byte } => {
6889 write!(
6890 f,
6891 "invalid base64 alphabet byte 0x{byte:02x} at index {index}"
6892 )
6893 }
6894 Self::PaddingByte { index } => {
6895 write!(f, "base64 alphabet contains padding byte at index {index}")
6896 }
6897 Self::DuplicateByte {
6898 first,
6899 second,
6900 byte,
6901 } => write!(
6902 f,
6903 "base64 alphabet byte 0x{byte:02x} is duplicated at indexes {first} and {second}"
6904 ),
6905 }
6906 }
6907}
6908
6909#[cfg(feature = "std")]
6910impl std::error::Error for AlphabetError {}
6911
6912#[derive(Clone, Copy, Debug, Eq, PartialEq)]
6914pub enum DecodeError {
6915 InvalidInput,
6918 InvalidLength,
6920 InvalidByte {
6922 index: usize,
6924 byte: u8,
6926 },
6927 InvalidPadding {
6929 index: usize,
6931 },
6932 InvalidLineWrap {
6934 index: usize,
6936 },
6937 OutputTooSmall {
6939 required: usize,
6941 available: usize,
6943 },
6944 StagingTooSmall {
6946 required: usize,
6948 available: usize,
6950 },
6951}
6952
6953impl core::fmt::Display for DecodeError {
6954 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
6955 match self {
6956 Self::InvalidInput => f.write_str("malformed base64 input"),
6957 Self::InvalidLength => f.write_str("invalid base64 input length"),
6958 Self::InvalidByte { index, byte } => {
6959 write!(f, "invalid base64 byte 0x{byte:02x} at index {index}")
6960 }
6961 Self::InvalidPadding { index } => write!(f, "invalid base64 padding at index {index}"),
6962 Self::InvalidLineWrap { index } => {
6963 write!(f, "invalid base64 line wrapping at index {index}")
6964 }
6965 Self::OutputTooSmall {
6966 required,
6967 available,
6968 } => write!(
6969 f,
6970 "base64 decode output buffer too small: required {required}, available {available}"
6971 ),
6972 Self::StagingTooSmall {
6973 required,
6974 available,
6975 } => write!(
6976 f,
6977 "base64 decode staging buffer too small: required {required}, available {available}"
6978 ),
6979 }
6980 }
6981}
6982
6983impl DecodeError {
6984 fn with_index_offset(self, offset: usize) -> Self {
6985 match self {
6986 Self::InvalidByte { index, byte } => Self::InvalidByte {
6987 index: index + offset,
6988 byte,
6989 },
6990 Self::InvalidPadding { index } => Self::InvalidPadding {
6991 index: index + offset,
6992 },
6993 Self::InvalidLineWrap { index } => Self::InvalidLineWrap {
6994 index: index + offset,
6995 },
6996 Self::InvalidInput
6997 | Self::InvalidLength
6998 | Self::OutputTooSmall { .. }
6999 | Self::StagingTooSmall { .. } => self,
7000 }
7001 }
7002}
7003
7004#[cfg(feature = "std")]
7005impl std::error::Error for DecodeError {}
7006
7007struct LegacyBytes<'a> {
7008 input: &'a [u8],
7009 index: usize,
7010}
7011
7012impl<'a> LegacyBytes<'a> {
7013 const fn new(input: &'a [u8]) -> Self {
7014 Self { input, index: 0 }
7015 }
7016
7017 fn next_byte(&mut self) -> Option<(usize, u8)> {
7018 while self.index < self.input.len() {
7019 let index = self.index;
7020 let byte = self.input[index];
7021 self.index += 1;
7022 if !is_legacy_whitespace(byte) {
7023 return Some((index, byte));
7024 }
7025 }
7026 None
7027 }
7028}
7029
7030fn validate_legacy_decode<A: Alphabet, const PAD: bool>(
7031 input: &[u8],
7032) -> Result<usize, DecodeError> {
7033 let mut bytes = LegacyBytes::new(input);
7034 let mut chunk = [0u8; 4];
7035 let mut indexes = [0usize; 4];
7036 let mut chunk_len = 0;
7037 let mut required = 0;
7038 let mut terminal_seen = false;
7039
7040 while let Some((index, byte)) = bytes.next_byte() {
7041 if terminal_seen {
7042 return Err(DecodeError::InvalidPadding { index });
7043 }
7044
7045 chunk[chunk_len] = byte;
7046 indexes[chunk_len] = index;
7047 chunk_len += 1;
7048
7049 if chunk_len == 4 {
7050 let written =
7051 validate_chunk::<A, PAD>(chunk).map_err(|err| map_chunk_error(err, &indexes))?;
7052 required += written;
7053 terminal_seen = written < 3;
7054 chunk_len = 0;
7055 }
7056 }
7057
7058 if chunk_len == 0 {
7059 return Ok(required);
7060 }
7061 if PAD {
7062 return Err(DecodeError::InvalidLength);
7063 }
7064
7065 validate_tail_unpadded::<A>(&chunk[..chunk_len])
7066 .map_err(|err| map_partial_chunk_error(err, &indexes, chunk_len))?;
7067 Ok(required + decoded_capacity(chunk_len))
7068}
7069
7070fn decode_legacy_to_slice<A: Alphabet, const PAD: bool>(
7071 input: &[u8],
7072 output: &mut [u8],
7073) -> Result<usize, DecodeError> {
7074 let mut bytes = LegacyBytes::new(input);
7075 let mut chunk = [0u8; 4];
7076 let mut indexes = [0usize; 4];
7077 let mut chunk_len = 0;
7078 let mut write = 0;
7079 let mut terminal_seen = false;
7080
7081 while let Some((index, byte)) = bytes.next_byte() {
7082 if terminal_seen {
7083 return Err(DecodeError::InvalidPadding { index });
7084 }
7085
7086 chunk[chunk_len] = byte;
7087 indexes[chunk_len] = index;
7088 chunk_len += 1;
7089
7090 if chunk_len == 4 {
7091 let available = output.len();
7092 let output_tail = output.get_mut(write..).ok_or(DecodeError::OutputTooSmall {
7093 required: write,
7094 available,
7095 })?;
7096 let written = decode_chunk::<A, PAD>(chunk, output_tail)
7097 .map_err(|err| map_chunk_error(err, &indexes))?;
7098 write += written;
7099 terminal_seen = written < 3;
7100 chunk_len = 0;
7101 }
7102 }
7103
7104 if chunk_len == 0 {
7105 return Ok(write);
7106 }
7107 if PAD {
7108 return Err(DecodeError::InvalidLength);
7109 }
7110
7111 decode_tail_unpadded::<A>(&chunk[..chunk_len], &mut output[write..])
7112 .map_err(|err| map_partial_chunk_error(err, &indexes, chunk_len))
7113 .map(|n| write + n)
7114}
7115
7116struct WrappedBytes<'a> {
7117 input: &'a [u8],
7118 wrap: LineWrap,
7119 index: usize,
7120 line_len: usize,
7121}
7122
7123impl<'a> WrappedBytes<'a> {
7124 const fn new(input: &'a [u8], wrap: LineWrap) -> Result<Self, DecodeError> {
7125 if wrap.line_len == 0 {
7126 return Err(DecodeError::InvalidLineWrap { index: 0 });
7127 }
7128 Ok(Self {
7129 input,
7130 wrap,
7131 index: 0,
7132 line_len: 0,
7133 })
7134 }
7135
7136 fn next_byte(&mut self) -> Result<Option<(usize, u8)>, DecodeError> {
7137 loop {
7138 if self.index == self.input.len() {
7139 return Ok(None);
7140 }
7141
7142 if self.starts_with_line_ending() {
7143 let line_end_index = self.index;
7144 if self.line_len == 0 {
7145 return Err(DecodeError::InvalidLineWrap {
7146 index: line_end_index,
7147 });
7148 }
7149
7150 self.index += self.wrap.line_ending.byte_len();
7151 if self.index == self.input.len() {
7152 self.line_len = 0;
7153 return Ok(None);
7154 }
7155
7156 if self.line_len != self.wrap.line_len {
7157 return Err(DecodeError::InvalidLineWrap {
7158 index: line_end_index,
7159 });
7160 }
7161 self.line_len = 0;
7162 continue;
7163 }
7164
7165 let byte = self.input[self.index];
7166 if matches!(byte, b'\r' | b'\n') {
7167 return Err(DecodeError::InvalidLineWrap { index: self.index });
7168 }
7169
7170 self.line_len += 1;
7171 if self.line_len > self.wrap.line_len {
7172 return Err(DecodeError::InvalidLineWrap { index: self.index });
7173 }
7174
7175 let index = self.index;
7176 self.index += 1;
7177 return Ok(Some((index, byte)));
7178 }
7179 }
7180
7181 fn starts_with_line_ending(&self) -> bool {
7182 let line_ending = self.wrap.line_ending.as_bytes();
7183 let Some(end) = self.index.checked_add(line_ending.len()) else {
7184 return false;
7185 };
7186 end <= self.input.len() && &self.input[self.index..end] == line_ending
7187 }
7188}
7189
7190fn validate_wrapped_decode<A: Alphabet, const PAD: bool>(
7191 input: &[u8],
7192 wrap: LineWrap,
7193) -> Result<usize, DecodeError> {
7194 let mut bytes = WrappedBytes::new(input, wrap)?;
7195 let mut chunk = [0u8; 4];
7196 let mut indexes = [0usize; 4];
7197 let mut chunk_len = 0;
7198 let mut required = 0;
7199 let mut terminal_seen = false;
7200
7201 while let Some((index, byte)) = bytes.next_byte()? {
7202 if terminal_seen {
7203 return Err(DecodeError::InvalidPadding { index });
7204 }
7205
7206 chunk[chunk_len] = byte;
7207 indexes[chunk_len] = index;
7208 chunk_len += 1;
7209
7210 if chunk_len == 4 {
7211 let written =
7212 validate_chunk::<A, PAD>(chunk).map_err(|err| map_chunk_error(err, &indexes))?;
7213 required += written;
7214 terminal_seen = written < 3;
7215 chunk_len = 0;
7216 }
7217 }
7218
7219 if chunk_len == 0 {
7220 return Ok(required);
7221 }
7222 if PAD {
7223 return Err(DecodeError::InvalidLength);
7224 }
7225
7226 validate_tail_unpadded::<A>(&chunk[..chunk_len])
7227 .map_err(|err| map_partial_chunk_error(err, &indexes, chunk_len))?;
7228 Ok(required + decoded_capacity(chunk_len))
7229}
7230
7231fn decode_wrapped_to_slice<A: Alphabet, const PAD: bool>(
7232 input: &[u8],
7233 output: &mut [u8],
7234 wrap: LineWrap,
7235) -> Result<usize, DecodeError> {
7236 let mut bytes = WrappedBytes::new(input, wrap)?;
7237 let mut chunk = [0u8; 4];
7238 let mut indexes = [0usize; 4];
7239 let mut chunk_len = 0;
7240 let mut write = 0;
7241 let mut terminal_seen = false;
7242
7243 while let Some((index, byte)) = bytes.next_byte()? {
7244 if terminal_seen {
7245 return Err(DecodeError::InvalidPadding { index });
7246 }
7247
7248 chunk[chunk_len] = byte;
7249 indexes[chunk_len] = index;
7250 chunk_len += 1;
7251
7252 if chunk_len == 4 {
7253 let available = output.len();
7254 let output_tail = output.get_mut(write..).ok_or(DecodeError::OutputTooSmall {
7255 required: write,
7256 available,
7257 })?;
7258 let written = decode_chunk::<A, PAD>(chunk, output_tail)
7259 .map_err(|err| map_chunk_error(err, &indexes))?;
7260 write += written;
7261 terminal_seen = written < 3;
7262 chunk_len = 0;
7263 }
7264 }
7265
7266 if chunk_len == 0 {
7267 return Ok(write);
7268 }
7269 if PAD {
7270 return Err(DecodeError::InvalidLength);
7271 }
7272
7273 decode_tail_unpadded::<A>(&chunk[..chunk_len], &mut output[write..])
7274 .map_err(|err| map_partial_chunk_error(err, &indexes, chunk_len))
7275 .map(|n| write + n)
7276}
7277
7278fn compact_wrapped_input(buffer: &mut [u8], wrap: LineWrap) -> Result<usize, DecodeError> {
7279 if !wrap.is_valid() {
7280 return Err(DecodeError::InvalidLineWrap { index: 0 });
7281 }
7282
7283 let line_ending = wrap.line_ending.as_bytes();
7284 let line_ending_len = line_ending.len();
7285 let mut read = 0;
7286 let mut write = 0;
7287
7288 while read < buffer.len() {
7289 let line_end = read + line_ending_len;
7290 if buffer.get(read..line_end) == Some(line_ending) {
7291 read = line_end;
7292 continue;
7293 }
7294
7295 buffer[write] = buffer[read];
7296 write += 1;
7297 read += 1;
7298 }
7299
7300 Ok(write)
7301}
7302
7303#[inline]
7304const fn is_legacy_whitespace(byte: u8) -> bool {
7305 matches!(byte, b' ' | b'\t' | b'\r' | b'\n')
7306}
7307
7308fn map_chunk_error(err: DecodeError, indexes: &[usize; 4]) -> DecodeError {
7309 match err {
7310 DecodeError::InvalidByte { index, byte } => DecodeError::InvalidByte {
7311 index: indexes[index],
7312 byte,
7313 },
7314 DecodeError::InvalidPadding { index } => DecodeError::InvalidPadding {
7315 index: indexes[index],
7316 },
7317 DecodeError::InvalidInput
7318 | DecodeError::InvalidLineWrap { .. }
7319 | DecodeError::InvalidLength
7320 | DecodeError::OutputTooSmall { .. }
7321 | DecodeError::StagingTooSmall { .. } => err,
7322 }
7323}
7324
7325fn map_partial_chunk_error(err: DecodeError, indexes: &[usize; 4], len: usize) -> DecodeError {
7326 match err {
7327 DecodeError::InvalidByte { index, byte } if index < len => DecodeError::InvalidByte {
7328 index: indexes[index],
7329 byte,
7330 },
7331 DecodeError::InvalidPadding { index } if index < len => DecodeError::InvalidPadding {
7332 index: indexes[index],
7333 },
7334 DecodeError::InvalidByte { .. }
7335 | DecodeError::InvalidPadding { .. }
7336 | DecodeError::InvalidLineWrap { .. }
7337 | DecodeError::InvalidInput
7338 | DecodeError::InvalidLength
7339 | DecodeError::OutputTooSmall { .. }
7340 | DecodeError::StagingTooSmall { .. } => err,
7341 }
7342}
7343
7344fn decode_padded<A: Alphabet>(input: &[u8], output: &mut [u8]) -> Result<usize, DecodeError> {
7345 if !input.len().is_multiple_of(4) {
7346 return Err(DecodeError::InvalidLength);
7347 }
7348 let required = decoded_len_padded(input)?;
7349 if output.len() < required {
7350 return Err(DecodeError::OutputTooSmall {
7351 required,
7352 available: output.len(),
7353 });
7354 }
7355
7356 let mut read = 0;
7357 let mut write = 0;
7358 while read < input.len() {
7359 let chunk = read_quad(input, read)?;
7360 let available = output.len();
7361 let output_tail = output.get_mut(write..).ok_or(DecodeError::OutputTooSmall {
7362 required: write,
7363 available,
7364 })?;
7365 let written = decode_chunk::<A, true>(chunk, output_tail)
7366 .map_err(|err| err.with_index_offset(read))?;
7367 read += 4;
7368 write += written;
7369 if written < 3 && read != input.len() {
7370 return Err(DecodeError::InvalidPadding { index: read - 4 });
7371 }
7372 }
7373 Ok(write)
7374}
7375
7376fn validate_decode<A: Alphabet, const PAD: bool>(input: &[u8]) -> Result<usize, DecodeError> {
7377 if input.is_empty() {
7378 return Ok(0);
7379 }
7380
7381 if PAD {
7382 validate_padded::<A>(input)
7383 } else {
7384 validate_unpadded::<A>(input)
7385 }
7386}
7387
7388fn validate_padded<A: Alphabet>(input: &[u8]) -> Result<usize, DecodeError> {
7389 if !input.len().is_multiple_of(4) {
7390 return Err(DecodeError::InvalidLength);
7391 }
7392 let required = decoded_len_padded(input)?;
7393
7394 let mut read = 0;
7395 while read < input.len() {
7396 let chunk = read_quad(input, read)?;
7397 let written =
7398 validate_chunk::<A, true>(chunk).map_err(|err| err.with_index_offset(read))?;
7399 read += 4;
7400 if written < 3 && read != input.len() {
7401 return Err(DecodeError::InvalidPadding { index: read - 4 });
7402 }
7403 }
7404
7405 Ok(required)
7406}
7407
7408fn validate_unpadded<A: Alphabet>(input: &[u8]) -> Result<usize, DecodeError> {
7409 let required = decoded_len_unpadded(input)?;
7410
7411 let mut read = 0;
7412 while read + 4 <= input.len() {
7413 let chunk = read_quad(input, read)?;
7414 validate_chunk::<A, false>(chunk).map_err(|err| err.with_index_offset(read))?;
7415 read += 4;
7416 }
7417 validate_tail_unpadded::<A>(&input[read..]).map_err(|err| err.with_index_offset(read))?;
7418
7419 Ok(required)
7420}
7421
7422fn decode_unpadded<A: Alphabet>(input: &[u8], output: &mut [u8]) -> Result<usize, DecodeError> {
7423 let required = decoded_len_unpadded(input)?;
7424 if output.len() < required {
7425 return Err(DecodeError::OutputTooSmall {
7426 required,
7427 available: output.len(),
7428 });
7429 }
7430
7431 let mut read = 0;
7432 let mut write = 0;
7433 while read + 4 <= input.len() {
7434 let chunk = read_quad(input, read)?;
7435 let available = output.len();
7436 let output_tail = output.get_mut(write..).ok_or(DecodeError::OutputTooSmall {
7437 required: write,
7438 available,
7439 })?;
7440 let written = decode_chunk::<A, false>(chunk, output_tail)
7441 .map_err(|err| err.with_index_offset(read))?;
7442 read += 4;
7443 write += written;
7444 }
7445 decode_tail_unpadded::<A>(&input[read..], &mut output[write..])
7446 .map_err(|err| err.with_index_offset(read))
7447 .map(|n| write + n)
7448}
7449
7450fn decoded_len_padded(input: &[u8]) -> Result<usize, DecodeError> {
7451 if input.is_empty() {
7452 return Ok(0);
7453 }
7454 if !input.len().is_multiple_of(4) {
7455 return Err(DecodeError::InvalidLength);
7456 }
7457
7458 let Some((&last, before_last_prefix)) = input.split_last() else {
7459 return Ok(0);
7460 };
7461 let Some(&before_last) = before_last_prefix.last() else {
7462 return Err(DecodeError::InvalidLength);
7463 };
7464
7465 let mut padding = 0;
7466 if last == b'=' {
7467 padding += 1;
7468 }
7469 if before_last == b'=' {
7470 padding += 1;
7471 }
7472 if padding == 0
7473 && let Some(index) = input.iter().position(|byte| *byte == b'=')
7474 {
7475 return Err(DecodeError::InvalidPadding { index });
7476 }
7477 if padding > 0 {
7478 let first_pad = input.len() - padding;
7479 if let Some(index) = input[..first_pad].iter().position(|byte| *byte == b'=') {
7480 return Err(DecodeError::InvalidPadding { index });
7481 }
7482 }
7483 Ok(input.len() / 4 * 3 - padding)
7484}
7485
7486fn decoded_len_unpadded(input: &[u8]) -> Result<usize, DecodeError> {
7487 if input.len() % 4 == 1 {
7488 return Err(DecodeError::InvalidLength);
7489 }
7490 if let Some(index) = input.iter().position(|byte| *byte == b'=') {
7491 return Err(DecodeError::InvalidPadding { index });
7492 }
7493 Ok(decoded_capacity(input.len()))
7494}
7495
7496fn read_quad(input: &[u8], offset: usize) -> Result<[u8; 4], DecodeError> {
7497 let end = offset.checked_add(4).ok_or(DecodeError::InvalidLength)?;
7498 match input.get(offset..end) {
7499 Some([b0, b1, b2, b3]) => Ok([*b0, *b1, *b2, *b3]),
7500 _ => Err(DecodeError::InvalidLength),
7501 }
7502}
7503
7504fn first_padding_index_unchecked(input: [u8; 4]) -> usize {
7505 let [b0, b1, b2, b3] = input;
7506 if b0 == b'=' {
7507 0
7508 } else if b1 == b'=' {
7509 1
7510 } else if b2 == b'=' {
7511 2
7512 } else if b3 == b'=' {
7513 3
7514 } else {
7515 debug_assert!(
7516 false,
7517 "first_padding_index_unchecked called with no padding"
7518 );
7519 4
7520 }
7521}
7522
7523fn validate_chunk<A: Alphabet, const PAD: bool>(input: [u8; 4]) -> Result<usize, DecodeError> {
7524 let [b0, b1, b2, b3] = input;
7525 let _v0 = decode_byte::<A>(b0, 0)?;
7526 let v1 = decode_byte::<A>(b1, 1)?;
7527
7528 match (b2, b3) {
7529 (b'=', b'=') if PAD => {
7530 if v1 & 0b0000_1111 != 0 {
7531 return Err(DecodeError::InvalidPadding { index: 1 });
7532 }
7533 Ok(1)
7534 }
7535 (b'=', _) if PAD => Err(DecodeError::InvalidPadding { index: 2 }),
7536 (_, b'=') if PAD => {
7537 let v2 = decode_byte::<A>(b2, 2)?;
7538 if v2 & 0b0000_0011 != 0 {
7539 return Err(DecodeError::InvalidPadding { index: 2 });
7540 }
7541 Ok(2)
7542 }
7543 (b'=', _) | (_, b'=') => Err(DecodeError::InvalidPadding {
7544 index: first_padding_index_unchecked(input),
7545 }),
7546 _ => {
7547 decode_byte::<A>(b2, 2)?;
7548 decode_byte::<A>(b3, 3)?;
7549 Ok(3)
7550 }
7551 }
7552}
7553
7554fn decode_chunk<A: Alphabet, const PAD: bool>(
7555 input: [u8; 4],
7556 output: &mut [u8],
7557) -> Result<usize, DecodeError> {
7558 let [b0, b1, b2, b3] = input;
7559 let v0 = decode_byte::<A>(b0, 0)?;
7560 let v1 = decode_byte::<A>(b1, 1)?;
7561
7562 match (b2, b3) {
7563 (b'=', b'=') if PAD => {
7564 if output.is_empty() {
7565 return Err(DecodeError::OutputTooSmall {
7566 required: 1,
7567 available: output.len(),
7568 });
7569 }
7570 if v1 & 0b0000_1111 != 0 {
7571 return Err(DecodeError::InvalidPadding { index: 1 });
7572 }
7573 output[0] = (v0 << 2) | (v1 >> 4);
7574 Ok(1)
7575 }
7576 (b'=', _) if PAD => Err(DecodeError::InvalidPadding { index: 2 }),
7577 (_, b'=') if PAD => {
7578 if output.len() < 2 {
7579 return Err(DecodeError::OutputTooSmall {
7580 required: 2,
7581 available: output.len(),
7582 });
7583 }
7584 let v2 = decode_byte::<A>(b2, 2)?;
7585 if v2 & 0b0000_0011 != 0 {
7586 return Err(DecodeError::InvalidPadding { index: 2 });
7587 }
7588 output[0] = (v0 << 2) | (v1 >> 4);
7589 output[1] = (v1 << 4) | (v2 >> 2);
7590 Ok(2)
7591 }
7592 (b'=', _) | (_, b'=') => Err(DecodeError::InvalidPadding {
7593 index: first_padding_index_unchecked(input),
7594 }),
7595 _ => {
7596 if output.len() < 3 {
7597 return Err(DecodeError::OutputTooSmall {
7598 required: 3,
7599 available: output.len(),
7600 });
7601 }
7602 let v2 = decode_byte::<A>(b2, 2)?;
7603 let v3 = decode_byte::<A>(b3, 3)?;
7604 output[0] = (v0 << 2) | (v1 >> 4);
7605 output[1] = (v1 << 4) | (v2 >> 2);
7606 output[2] = (v2 << 6) | v3;
7607 Ok(3)
7608 }
7609 }
7610}
7611
7612fn validate_tail_unpadded<A: Alphabet>(input: &[u8]) -> Result<(), DecodeError> {
7613 match input {
7614 [] => Ok(()),
7615 [b0, b1] => {
7616 decode_byte::<A>(*b0, 0)?;
7617 let v1 = decode_byte::<A>(*b1, 1)?;
7618 if v1 & 0b0000_1111 != 0 {
7619 return Err(DecodeError::InvalidPadding { index: 1 });
7620 }
7621 Ok(())
7622 }
7623 [b0, b1, b2] => {
7624 decode_byte::<A>(*b0, 0)?;
7625 decode_byte::<A>(*b1, 1)?;
7626 let v2 = decode_byte::<A>(*b2, 2)?;
7627 if v2 & 0b0000_0011 != 0 {
7628 return Err(DecodeError::InvalidPadding { index: 2 });
7629 }
7630 Ok(())
7631 }
7632 _ => Err(DecodeError::InvalidLength),
7633 }
7634}
7635
7636fn decode_tail_unpadded<A: Alphabet>(
7637 input: &[u8],
7638 output: &mut [u8],
7639) -> Result<usize, DecodeError> {
7640 match input {
7641 [] => Ok(0),
7642 [b0, b1] => {
7643 let Some(out0) = output.first_mut() else {
7644 return Err(DecodeError::OutputTooSmall {
7645 required: 1,
7646 available: output.len(),
7647 });
7648 };
7649 let v0 = decode_byte::<A>(*b0, 0)?;
7650 let v1 = decode_byte::<A>(*b1, 1)?;
7651 if v1 & 0b0000_1111 != 0 {
7652 return Err(DecodeError::InvalidPadding { index: 1 });
7653 }
7654 *out0 = (v0 << 2) | (v1 >> 4);
7655 Ok(1)
7656 }
7657 [b0, b1, b2] => {
7658 let available = output.len();
7659 let Some([out0, out1]) = output.get_mut(..2) else {
7660 return Err(DecodeError::OutputTooSmall {
7661 required: 2,
7662 available,
7663 });
7664 };
7665 let v0 = decode_byte::<A>(*b0, 0)?;
7666 let v1 = decode_byte::<A>(*b1, 1)?;
7667 let v2 = decode_byte::<A>(*b2, 2)?;
7668 if v2 & 0b0000_0011 != 0 {
7669 return Err(DecodeError::InvalidPadding { index: 2 });
7670 }
7671 *out0 = (v0 << 2) | (v1 >> 4);
7672 *out1 = (v1 << 4) | (v2 >> 2);
7673 Ok(2)
7674 }
7675 _ => Err(DecodeError::InvalidLength),
7676 }
7677}
7678
7679fn decode_byte<A: Alphabet>(byte: u8, index: usize) -> Result<u8, DecodeError> {
7680 A::decode(byte).ok_or(DecodeError::InvalidByte { index, byte })
7681}
7682
7683fn ct_decode_slice<A: Alphabet, const PAD: bool>(
7684 input: &[u8],
7685 output: &mut [u8],
7686) -> Result<usize, DecodeError> {
7687 if input.is_empty() {
7688 return Ok(0);
7689 }
7690
7691 if PAD {
7692 ct_decode_padded::<A>(input, output)
7693 } else {
7694 ct_decode_unpadded::<A>(input, output)
7695 }
7696}
7697
7698fn ct_decode_slice_staged_clear_tail<A: Alphabet, const PAD: bool>(
7699 input: &[u8],
7700 output: &mut [u8],
7701 staging: &mut [u8],
7702) -> Result<usize, DecodeError> {
7703 let required = match ct_decoded_len::<A, PAD>(input) {
7704 Ok(required) => required,
7705 Err(err) => {
7706 wipe_bytes(output);
7707 wipe_bytes(staging);
7708 return Err(err);
7709 }
7710 };
7711
7712 if output.len() < required {
7713 wipe_bytes(output);
7714 wipe_bytes(staging);
7715 return Err(DecodeError::OutputTooSmall {
7716 required,
7717 available: output.len(),
7718 });
7719 }
7720
7721 if staging.len() < required {
7722 wipe_bytes(output);
7723 wipe_bytes(staging);
7724 return Err(DecodeError::StagingTooSmall {
7725 required,
7726 available: staging.len(),
7727 });
7728 }
7729
7730 let written = match ct_decode_slice::<A, PAD>(input, &mut staging[..required]) {
7731 Ok(written) => written,
7732 Err(err) => {
7733 wipe_bytes(output);
7734 wipe_bytes(staging);
7735 return Err(err);
7736 }
7737 };
7738
7739 output[..written].copy_from_slice(&staging[..written]);
7740 wipe_bytes(staging);
7741 wipe_tail(output, written);
7742 Ok(written)
7743}
7744
7745fn ct_decode_in_place<A: Alphabet, const PAD: bool>(
7746 buffer: &mut [u8],
7747) -> Result<usize, DecodeError> {
7748 if buffer.is_empty() {
7749 return Ok(0);
7750 }
7751
7752 if PAD {
7753 ct_decode_padded_in_place::<A>(buffer)
7754 } else {
7755 ct_decode_unpadded_in_place::<A>(buffer)
7756 }
7757}
7758
7759#[inline(never)]
7760#[allow(unsafe_code)]
7761fn ct_error_gate_barrier(invalid_byte: u8, invalid_padding: u8) {
7762 core::hint::black_box(invalid_byte | invalid_padding);
7763 core::sync::atomic::compiler_fence(core::sync::atomic::Ordering::SeqCst);
7764
7765 #[cfg(all(not(miri), any(target_arch = "x86", target_arch = "x86_64")))]
7766 {
7767 unsafe {
7770 core::arch::asm!("lfence", options(nostack, preserves_flags, nomem));
7771 }
7772 }
7773
7774 #[cfg(all(not(miri), target_arch = "aarch64"))]
7775 {
7776 unsafe {
7780 core::arch::asm!("isb sy", "hint #20", options(nostack, preserves_flags));
7781 }
7782 }
7783
7784 #[cfg(all(not(miri), target_arch = "arm"))]
7785 {
7786 unsafe {
7789 core::arch::asm!("isb sy", options(nostack, preserves_flags));
7790 }
7791 }
7792
7793 #[cfg(all(not(miri), any(target_arch = "riscv32", target_arch = "riscv64")))]
7794 {
7795 unsafe {
7800 core::arch::asm!("fence rw, rw", options(nostack, preserves_flags));
7801 }
7802 }
7803}
7804
7805fn ct_validate_decode<A: Alphabet, const PAD: bool>(input: &[u8]) -> Result<(), DecodeError> {
7806 if input.is_empty() {
7807 return Ok(());
7808 }
7809
7810 if PAD {
7811 ct_validate_padded::<A>(input)
7812 } else {
7813 ct_validate_unpadded::<A>(input)
7814 }
7815}
7816
7817fn ct_decoded_len<A: Alphabet, const PAD: bool>(input: &[u8]) -> Result<usize, DecodeError> {
7818 ct_validate_decode::<A, PAD>(input)?;
7819 if input.is_empty() {
7820 return Ok(0);
7821 }
7822
7823 if PAD {
7824 Ok(input.len() / 4 * 3 - ct_padding_len(input))
7825 } else {
7826 let full_quads = input.len() / 4 * 3;
7827 match input.len() % 4 {
7828 0 => Ok(full_quads),
7829 2 => Ok(full_quads + 1),
7830 3 => Ok(full_quads + 2),
7831 _ => Err(DecodeError::InvalidLength),
7832 }
7833 }
7834}
7835
7836fn ct_validate_padded<A: Alphabet>(input: &[u8]) -> Result<(), DecodeError> {
7837 if !input.len().is_multiple_of(4) {
7838 return Err(DecodeError::InvalidLength);
7839 }
7840
7841 let padding = ct_padding_len(input);
7842 let mut invalid_byte = 0u8;
7843 let mut invalid_padding = 0u8;
7844 let mut read = 0;
7845
7846 while read + 4 < input.len() {
7847 let [b0, b1, b2, b3] =
7848 read_quad_or_mark_invalid(input, read, &mut invalid_byte, &mut invalid_padding);
7849 let (_, valid0) = ct_decode_alphabet_byte::<A>(b0);
7850 let (_, valid1) = ct_decode_alphabet_byte::<A>(b1);
7851 let (_, valid2) = ct_decode_alphabet_byte::<A>(b2);
7852 let (_, valid3) = ct_decode_alphabet_byte::<A>(b3);
7853
7854 invalid_byte |= !valid0;
7855 invalid_byte |= !valid1;
7856 invalid_byte |= !valid2;
7857 invalid_byte |= !valid3;
7858 invalid_padding |= ct_mask_eq_u8(b2, b'=');
7859 invalid_padding |= ct_mask_eq_u8(b3, b'=');
7860 read += 4;
7861 }
7862
7863 let final_chunk =
7864 read_quad_or_mark_invalid(input, read, &mut invalid_byte, &mut invalid_padding);
7865 let (_, final_invalid_byte, final_invalid_padding, _) =
7866 ct_padded_final_quantum::<A>(final_chunk, padding);
7867 invalid_byte |= final_invalid_byte;
7868 invalid_padding |= final_invalid_padding;
7869
7870 report_ct_error(invalid_byte, invalid_padding)
7871}
7872
7873fn ct_validate_unpadded<A: Alphabet>(input: &[u8]) -> Result<(), DecodeError> {
7874 if input.len() % 4 == 1 {
7875 return Err(DecodeError::InvalidLength);
7876 }
7877
7878 let mut invalid_byte = 0u8;
7879 let mut invalid_padding = 0u8;
7880 let mut read = 0;
7881
7882 while read + 4 <= input.len() {
7883 let [b0, b1, b2, b3] =
7884 read_quad_or_mark_invalid(input, read, &mut invalid_byte, &mut invalid_padding);
7885 let (_, valid0) = ct_decode_alphabet_byte::<A>(b0);
7886 let (_, valid1) = ct_decode_alphabet_byte::<A>(b1);
7887 let (_, valid2) = ct_decode_alphabet_byte::<A>(b2);
7888 let (_, valid3) = ct_decode_alphabet_byte::<A>(b3);
7889
7890 invalid_byte |= !valid0;
7891 invalid_byte |= !valid1;
7892 invalid_byte |= !valid2;
7893 invalid_byte |= !valid3;
7894 invalid_padding |= ct_mask_eq_u8(b0, b'=');
7895 invalid_padding |= ct_mask_eq_u8(b1, b'=');
7896 invalid_padding |= ct_mask_eq_u8(b2, b'=');
7897 invalid_padding |= ct_mask_eq_u8(b3, b'=');
7898
7899 read += 4;
7900 }
7901
7902 match read_tail_or_mark_invalid(input, read, &mut invalid_byte, &mut invalid_padding) {
7903 [] => {}
7904 [b0, b1] => {
7905 let (_, valid0) = ct_decode_alphabet_byte::<A>(*b0);
7906 let (v1, valid1) = ct_decode_alphabet_byte::<A>(*b1);
7907 invalid_byte |= !valid0;
7908 invalid_byte |= !valid1;
7909 invalid_padding |= ct_mask_eq_u8(*b0, b'=');
7910 invalid_padding |= ct_mask_eq_u8(*b1, b'=');
7911 invalid_padding |= ct_mask_nonzero_u8(v1 & 0b0000_1111);
7912 }
7913 [b0, b1, b2] => {
7914 let (_, valid0) = ct_decode_alphabet_byte::<A>(*b0);
7915 let (_, valid1) = ct_decode_alphabet_byte::<A>(*b1);
7916 let (v2, valid2) = ct_decode_alphabet_byte::<A>(*b2);
7917 invalid_byte |= !valid0;
7918 invalid_byte |= !valid1;
7919 invalid_byte |= !valid2;
7920 invalid_padding |= ct_mask_eq_u8(*b0, b'=');
7921 invalid_padding |= ct_mask_eq_u8(*b1, b'=');
7922 invalid_padding |= ct_mask_eq_u8(*b2, b'=');
7923 invalid_padding |= ct_mask_nonzero_u8(v2 & 0b0000_0011);
7924 }
7925 _ => {
7926 invalid_byte = 0xff;
7927 invalid_padding = 0xff;
7928 }
7929 }
7930
7931 report_ct_error(invalid_byte, invalid_padding)
7932}
7933
7934fn ct_padded_final_quantum<A: Alphabet>(
7935 input: [u8; 4],
7936 padding: usize,
7937) -> ([u8; 3], u8, u8, usize) {
7938 let [b0, b1, b2, b3] = input;
7939 let (v0, valid0) = ct_decode_alphabet_byte::<A>(b0);
7940 let (v1, valid1) = ct_decode_alphabet_byte::<A>(b1);
7941 let (v2, valid2) = ct_decode_alphabet_byte::<A>(b2);
7942 let (v3, valid3) = ct_decode_alphabet_byte::<A>(b3);
7943
7944 let padding_byte = match padding {
7945 0 => 0,
7946 1 => 1,
7947 2 => 2,
7948 _ => return ([0; 3], 0xff, 0xff, 0),
7949 };
7950 let no_padding = ct_mask_eq_u8(padding_byte, 0);
7951 let one_padding = ct_mask_eq_u8(padding_byte, 1);
7952 let two_padding = ct_mask_eq_u8(padding_byte, 2);
7953 let require_v2 = no_padding | one_padding;
7954 let require_v3 = no_padding;
7955
7956 let invalid_byte = !valid0 | !valid1 | (!valid2 & require_v2) | (!valid3 & require_v3);
7957 let invalid_padding = (ct_mask_nonzero_u8(v1 & 0b0000_1111) & two_padding)
7958 | ((ct_mask_eq_u8(b2, b'=') | ct_mask_nonzero_u8(v2 & 0b0000_0011)) & one_padding)
7959 | ((ct_mask_eq_u8(b2, b'=') | ct_mask_eq_u8(b3, b'=')) & no_padding);
7960
7961 (
7962 [(v0 << 2) | (v1 >> 4), (v1 << 4) | (v2 >> 2), (v2 << 6) | v3],
7963 invalid_byte,
7964 invalid_padding,
7965 3 - padding,
7966 )
7967}
7968
7969fn ct_decode_padded<A: Alphabet>(input: &[u8], output: &mut [u8]) -> Result<usize, DecodeError> {
7970 if !input.len().is_multiple_of(4) {
7971 return Err(DecodeError::InvalidLength);
7972 }
7973
7974 let padding = ct_padding_len(input);
7975 let required = input.len() / 4 * 3 - padding;
7976 if output.len() < required {
7977 return Err(DecodeError::OutputTooSmall {
7978 required,
7979 available: output.len(),
7980 });
7981 }
7982
7983 let mut invalid_byte = 0u8;
7984 let mut invalid_padding = 0u8;
7985 let mut write = 0;
7986 let mut read = 0;
7987
7988 while read + 4 < input.len() {
7989 let [b0, b1, b2, b3] =
7990 read_quad_or_mark_invalid(input, read, &mut invalid_byte, &mut invalid_padding);
7991 let (v0, valid0) = ct_decode_alphabet_byte::<A>(b0);
7992 let (v1, valid1) = ct_decode_alphabet_byte::<A>(b1);
7993 let (v2, valid2) = ct_decode_alphabet_byte::<A>(b2);
7994 let (v3, valid3) = ct_decode_alphabet_byte::<A>(b3);
7995
7996 invalid_byte |= !valid0;
7997 invalid_byte |= !valid1;
7998 invalid_byte |= !valid2;
7999 invalid_byte |= !valid3;
8000 invalid_padding |= ct_mask_eq_u8(b2, b'=');
8001 invalid_padding |= ct_mask_eq_u8(b3, b'=');
8002 output[write] = (v0 << 2) | (v1 >> 4);
8003 output[write + 1] = (v1 << 4) | (v2 >> 2);
8004 output[write + 2] = (v2 << 6) | v3;
8005 write += 3;
8006 read += 4;
8007 }
8008
8009 let final_chunk =
8010 read_quad_or_mark_invalid(input, read, &mut invalid_byte, &mut invalid_padding);
8011 let (final_bytes, final_invalid_byte, final_invalid_padding, final_written) =
8012 ct_padded_final_quantum::<A>(final_chunk, padding);
8013 invalid_byte |= final_invalid_byte;
8014 invalid_padding |= final_invalid_padding;
8015 output[write..write + final_written].copy_from_slice(&final_bytes[..final_written]);
8016 write += final_written;
8017
8018 report_ct_error(invalid_byte, invalid_padding)?;
8019 Ok(write)
8020}
8021
8022fn ct_decode_padded_in_place<A: Alphabet>(buffer: &mut [u8]) -> Result<usize, DecodeError> {
8023 if !buffer.len().is_multiple_of(4) {
8024 return Err(DecodeError::InvalidLength);
8025 }
8026
8027 let padding = ct_padding_len(buffer);
8028 let required = buffer.len() / 4 * 3 - padding;
8029 if required > buffer.len() {
8030 wipe_bytes(buffer);
8031 return Err(DecodeError::InvalidInput);
8032 }
8033
8034 let mut invalid_byte = 0u8;
8035 let mut invalid_padding = 0u8;
8036 let mut write = 0;
8037 let mut read = 0;
8038
8039 while read + 4 < buffer.len() {
8040 let [b0, b1, b2, b3] =
8041 read_quad_or_mark_invalid(buffer, read, &mut invalid_byte, &mut invalid_padding);
8042 let (v0, valid0) = ct_decode_alphabet_byte::<A>(b0);
8043 let (v1, valid1) = ct_decode_alphabet_byte::<A>(b1);
8044 let (v2, valid2) = ct_decode_alphabet_byte::<A>(b2);
8045 let (v3, valid3) = ct_decode_alphabet_byte::<A>(b3);
8046
8047 invalid_byte |= !valid0;
8048 invalid_byte |= !valid1;
8049 invalid_byte |= !valid2;
8050 invalid_byte |= !valid3;
8051 invalid_padding |= ct_mask_eq_u8(b2, b'=');
8052 invalid_padding |= ct_mask_eq_u8(b3, b'=');
8053 buffer[write] = (v0 << 2) | (v1 >> 4);
8054 buffer[write + 1] = (v1 << 4) | (v2 >> 2);
8055 buffer[write + 2] = (v2 << 6) | v3;
8056 write += 3;
8057 read += 4;
8058 }
8059
8060 let final_chunk =
8061 read_quad_or_mark_invalid(buffer, read, &mut invalid_byte, &mut invalid_padding);
8062 let (final_bytes, final_invalid_byte, final_invalid_padding, final_written) =
8063 ct_padded_final_quantum::<A>(final_chunk, padding);
8064 invalid_byte |= final_invalid_byte;
8065 invalid_padding |= final_invalid_padding;
8066 buffer[write..write + final_written].copy_from_slice(&final_bytes[..final_written]);
8067 write += final_written;
8068
8069 if write != required {
8070 ct_error_gate_barrier(invalid_byte, invalid_padding);
8071 wipe_bytes(buffer);
8072 return Err(DecodeError::InvalidInput);
8073 }
8074 if let Err(err) = report_ct_error(invalid_byte, invalid_padding) {
8075 wipe_bytes(buffer);
8076 return Err(err);
8077 }
8078 Ok(write)
8079}
8080
8081fn ct_decode_unpadded<A: Alphabet>(input: &[u8], output: &mut [u8]) -> Result<usize, DecodeError> {
8082 if input.len() % 4 == 1 {
8083 return Err(DecodeError::InvalidLength);
8084 }
8085
8086 let required = decoded_capacity(input.len());
8087 if output.len() < required {
8088 return Err(DecodeError::OutputTooSmall {
8089 required,
8090 available: output.len(),
8091 });
8092 }
8093
8094 let mut invalid_byte = 0u8;
8095 let mut invalid_padding = 0u8;
8096 let mut write = 0;
8097 let mut read = 0;
8098
8099 while read + 4 <= input.len() {
8100 let [b0, b1, b2, b3] =
8101 read_quad_or_mark_invalid(input, read, &mut invalid_byte, &mut invalid_padding);
8102 let (v0, valid0) = ct_decode_alphabet_byte::<A>(b0);
8103 let (v1, valid1) = ct_decode_alphabet_byte::<A>(b1);
8104 let (v2, valid2) = ct_decode_alphabet_byte::<A>(b2);
8105 let (v3, valid3) = ct_decode_alphabet_byte::<A>(b3);
8106
8107 invalid_byte |= !valid0;
8108 invalid_byte |= !valid1;
8109 invalid_byte |= !valid2;
8110 invalid_byte |= !valid3;
8111 invalid_padding |= ct_mask_eq_u8(b0, b'=');
8112 invalid_padding |= ct_mask_eq_u8(b1, b'=');
8113 invalid_padding |= ct_mask_eq_u8(b2, b'=');
8114 invalid_padding |= ct_mask_eq_u8(b3, b'=');
8115
8116 output[write] = (v0 << 2) | (v1 >> 4);
8117 output[write + 1] = (v1 << 4) | (v2 >> 2);
8118 output[write + 2] = (v2 << 6) | v3;
8119 read += 4;
8120 write += 3;
8121 }
8122
8123 match read_tail_or_mark_invalid(input, read, &mut invalid_byte, &mut invalid_padding) {
8124 [] => {}
8125 [b0, b1] => {
8126 let (v0, valid0) = ct_decode_alphabet_byte::<A>(*b0);
8127 let (v1, valid1) = ct_decode_alphabet_byte::<A>(*b1);
8128 invalid_byte |= !valid0;
8129 invalid_byte |= !valid1;
8130 invalid_padding |= ct_mask_eq_u8(*b0, b'=');
8131 invalid_padding |= ct_mask_eq_u8(*b1, b'=');
8132 invalid_padding |= ct_mask_nonzero_u8(v1 & 0b0000_1111);
8133 output[write] = (v0 << 2) | (v1 >> 4);
8134 write += 1;
8135 }
8136 [b0, b1, b2] => {
8137 let (v0, valid0) = ct_decode_alphabet_byte::<A>(*b0);
8138 let (v1, valid1) = ct_decode_alphabet_byte::<A>(*b1);
8139 let (v2, valid2) = ct_decode_alphabet_byte::<A>(*b2);
8140 invalid_byte |= !valid0;
8141 invalid_byte |= !valid1;
8142 invalid_byte |= !valid2;
8143 invalid_padding |= ct_mask_eq_u8(*b0, b'=');
8144 invalid_padding |= ct_mask_eq_u8(*b1, b'=');
8145 invalid_padding |= ct_mask_eq_u8(*b2, b'=');
8146 invalid_padding |= ct_mask_nonzero_u8(v2 & 0b0000_0011);
8147 output[write] = (v0 << 2) | (v1 >> 4);
8148 output[write + 1] = (v1 << 4) | (v2 >> 2);
8149 write += 2;
8150 }
8151 _ => {
8152 invalid_byte = 0xff;
8153 invalid_padding = 0xff;
8154 }
8155 }
8156
8157 report_ct_error(invalid_byte, invalid_padding)?;
8158 Ok(write)
8159}
8160
8161fn ct_decode_unpadded_in_place<A: Alphabet>(buffer: &mut [u8]) -> Result<usize, DecodeError> {
8162 if buffer.len() % 4 == 1 {
8163 return Err(DecodeError::InvalidLength);
8164 }
8165
8166 let required = decoded_capacity(buffer.len());
8167 if required > buffer.len() {
8168 wipe_bytes(buffer);
8169 return Err(DecodeError::InvalidInput);
8170 }
8171
8172 let mut invalid_byte = 0u8;
8173 let mut invalid_padding = 0u8;
8174 let mut write = 0;
8175 let mut read = 0;
8176
8177 while read + 4 <= buffer.len() {
8178 let [b0, b1, b2, b3] =
8179 read_quad_or_mark_invalid(buffer, read, &mut invalid_byte, &mut invalid_padding);
8180 let (v0, valid0) = ct_decode_alphabet_byte::<A>(b0);
8181 let (v1, valid1) = ct_decode_alphabet_byte::<A>(b1);
8182 let (v2, valid2) = ct_decode_alphabet_byte::<A>(b2);
8183 let (v3, valid3) = ct_decode_alphabet_byte::<A>(b3);
8184
8185 invalid_byte |= !valid0;
8186 invalid_byte |= !valid1;
8187 invalid_byte |= !valid2;
8188 invalid_byte |= !valid3;
8189 invalid_padding |= ct_mask_eq_u8(b0, b'=');
8190 invalid_padding |= ct_mask_eq_u8(b1, b'=');
8191 invalid_padding |= ct_mask_eq_u8(b2, b'=');
8192 invalid_padding |= ct_mask_eq_u8(b3, b'=');
8193
8194 buffer[write] = (v0 << 2) | (v1 >> 4);
8195 buffer[write + 1] = (v1 << 4) | (v2 >> 2);
8196 buffer[write + 2] = (v2 << 6) | v3;
8197 read += 4;
8198 write += 3;
8199 }
8200
8201 let tail = read_tail_or_mark_invalid(buffer, read, &mut invalid_byte, &mut invalid_padding);
8202 match tail {
8203 [] => {}
8204 [b0, b1] => {
8205 let (v0, valid0) = ct_decode_alphabet_byte::<A>(*b0);
8206 let (v1, valid1) = ct_decode_alphabet_byte::<A>(*b1);
8207 invalid_byte |= !valid0;
8208 invalid_byte |= !valid1;
8209 invalid_padding |= ct_mask_eq_u8(*b0, b'=');
8210 invalid_padding |= ct_mask_eq_u8(*b1, b'=');
8211 invalid_padding |= ct_mask_nonzero_u8(v1 & 0b0000_1111);
8212 buffer[write] = (v0 << 2) | (v1 >> 4);
8213 write += 1;
8214 }
8215 [b0, b1, b2] => {
8216 let (v0, valid0) = ct_decode_alphabet_byte::<A>(*b0);
8217 let (v1, valid1) = ct_decode_alphabet_byte::<A>(*b1);
8218 let (v2, valid2) = ct_decode_alphabet_byte::<A>(*b2);
8219 invalid_byte |= !valid0;
8220 invalid_byte |= !valid1;
8221 invalid_byte |= !valid2;
8222 invalid_padding |= ct_mask_eq_u8(*b0, b'=');
8223 invalid_padding |= ct_mask_eq_u8(*b1, b'=');
8224 invalid_padding |= ct_mask_eq_u8(*b2, b'=');
8225 invalid_padding |= ct_mask_nonzero_u8(v2 & 0b0000_0011);
8226 buffer[write] = (v0 << 2) | (v1 >> 4);
8227 buffer[write + 1] = (v1 << 4) | (v2 >> 2);
8228 write += 2;
8229 }
8230 _ => {
8231 invalid_byte = 0xff;
8232 invalid_padding = 0xff;
8233 }
8234 }
8235
8236 if write != required {
8237 ct_error_gate_barrier(invalid_byte, invalid_padding);
8238 wipe_bytes(buffer);
8239 return Err(DecodeError::InvalidInput);
8240 }
8241 if let Err(err) = report_ct_error(invalid_byte, invalid_padding) {
8242 wipe_bytes(buffer);
8243 return Err(err);
8244 }
8245 Ok(write)
8246}
8247
8248fn read_tail(input: &[u8], offset: usize) -> Result<&[u8], DecodeError> {
8249 input.get(offset..).ok_or(DecodeError::InvalidLength)
8250}
8251
8252fn read_quad_or_mark_invalid(
8253 input: &[u8],
8254 offset: usize,
8255 invalid_byte: &mut u8,
8256 invalid_padding: &mut u8,
8257) -> [u8; 4] {
8258 if let Ok(quad) = read_quad(input, offset) {
8259 quad
8260 } else {
8261 debug_assert!(
8262 false,
8263 "read_quad failed inside length-validated constant-time decode loop"
8264 );
8265 *invalid_byte = 0xff;
8266 *invalid_padding = 0xff;
8267 [0; 4]
8268 }
8269}
8270
8271fn read_tail_or_mark_invalid<'a>(
8272 input: &'a [u8],
8273 offset: usize,
8274 invalid_byte: &mut u8,
8275 invalid_padding: &mut u8,
8276) -> &'a [u8] {
8277 if let Ok(tail) = read_tail(input, offset) {
8278 tail
8279 } else {
8280 debug_assert!(
8281 false,
8282 "read_tail failed inside length-validated constant-time decode loop"
8283 );
8284 *invalid_byte = 0xff;
8285 *invalid_padding = 0xff;
8286 &[]
8287 }
8288}
8289
8290#[inline(never)]
8291#[allow(unsafe_code)]
8292fn ct_decode_alphabet_byte<A: Alphabet>(byte: u8) -> (u8, u8) {
8293 let mut decoded = 0u8;
8294 let mut valid = 0u8;
8295 let mut candidate = 0u8;
8296
8297 while candidate < 64 {
8298 let matches = core::hint::black_box(ct_mask_eq_u8(
8299 core::hint::black_box(byte),
8300 core::hint::black_box(A::ENCODE[candidate as usize]),
8301 ));
8302 decoded = core::hint::black_box(
8303 core::hint::black_box(decoded) | core::hint::black_box(candidate & matches),
8304 );
8305 decoded = unsafe { core::ptr::read_volatile(&raw const decoded) };
8309 valid =
8310 core::hint::black_box(core::hint::black_box(valid) | core::hint::black_box(matches));
8311 valid = unsafe { core::ptr::read_volatile(&raw const valid) };
8315 candidate += 1;
8316 }
8317
8318 (decoded, valid)
8319}
8320
8321fn ct_padding_len(input: &[u8]) -> usize {
8322 let Some((&last, before_last_prefix)) = input.split_last() else {
8323 return 0;
8324 };
8325 let Some(&before_last) = before_last_prefix.last() else {
8326 return 0;
8327 };
8328 usize::from(ct_mask_eq_u8(last, b'=') & 1) + usize::from(ct_mask_eq_u8(before_last, b'=') & 1)
8329}
8330
8331fn report_ct_error(invalid_byte: u8, invalid_padding: u8) -> Result<(), DecodeError> {
8332 ct_error_gate_barrier(invalid_byte, invalid_padding);
8333
8334 if (invalid_byte | invalid_padding) != 0 {
8335 Err(DecodeError::InvalidInput)
8336 } else {
8337 Ok(())
8338 }
8339}
8340
8341#[cfg(kani)]
8342mod kani_proofs {
8343 use super::{
8344 STANDARD, Standard, checked_encoded_len, ct, decode_byte, decode_chunk,
8345 decode_tail_unpadded, decoded_capacity, validate_tail_unpadded,
8346 };
8347
8348 #[kani::proof]
8349 fn checked_encoded_len_is_bounded_for_small_inputs() {
8350 let len = usize::from(kani::any::<u8>());
8351 let padded = kani::any::<bool>();
8352 let encoded = checked_encoded_len(len, padded).expect("u8 input length cannot overflow");
8353
8354 assert!(encoded >= len);
8355 assert!(encoded <= len / 3 * 4 + 4);
8356 }
8357
8358 #[kani::proof]
8359 fn decoded_capacity_is_bounded_for_small_inputs() {
8360 let len = usize::from(kani::any::<u8>());
8361 let capacity = decoded_capacity(len);
8362
8363 assert!(capacity <= len / 4 * 3 + 2);
8364 }
8365
8366 #[kani::proof]
8367 #[kani::unwind(3)]
8368 fn standard_in_place_decode_returns_prefix_within_buffer() {
8369 let mut buffer = kani::any::<[u8; 8]>();
8370 let result = STANDARD.decode_in_place(&mut buffer);
8371
8372 if let Ok(decoded) = result {
8373 assert!(decoded.len() <= 8);
8374 }
8375 }
8376
8377 #[kani::proof]
8378 #[kani::unwind(3)]
8379 fn standard_decode_slice_returns_written_within_output() {
8380 let input = kani::any::<[u8; 4]>();
8381 let mut output = kani::any::<[u8; 3]>();
8382 let result = STANDARD.decode_slice(&input, &mut output);
8383
8384 if let Ok(written) = result {
8385 assert!(written <= output.len());
8386 }
8387 }
8388
8389 #[kani::proof]
8390 #[kani::unwind(3)]
8391 fn standard_decode_chunk_returns_written_within_output() {
8392 let input = kani::any::<[u8; 4]>();
8393 let mut output = kani::any::<[u8; 3]>();
8394 let result = decode_chunk::<Standard, true>(input, &mut output);
8395
8396 if let Ok(written) = result {
8397 assert!(written <= output.len());
8398 assert!(written <= 3);
8399 }
8400 }
8401
8402 #[kani::proof]
8403 #[kani::unwind(3)]
8404 fn standard_decode_chunk_bit_packing_matches_decoded_values() {
8405 let input = kani::any::<[u8; 4]>();
8406 let mut output = kani::any::<[u8; 3]>();
8407 let result = decode_chunk::<Standard, true>(input, &mut output);
8408
8409 if let Ok(written) = result {
8410 let v0 = decode_byte::<Standard>(input[0], 0).expect("successful chunk has v0");
8411 let v1 = decode_byte::<Standard>(input[1], 1).expect("successful chunk has v1");
8412
8413 assert!(output[0] == ((v0 << 2) | (v1 >> 4)));
8414
8415 if written >= 2 {
8416 let v2 = decode_byte::<Standard>(input[2], 2).expect("successful chunk has v2");
8417 assert!(output[1] == ((v1 << 4) | (v2 >> 2)));
8418 }
8419
8420 if written == 3 {
8421 let v2 = decode_byte::<Standard>(input[2], 2).expect("successful chunk has v2");
8422 let v3 = decode_byte::<Standard>(input[3], 3).expect("successful chunk has v3");
8423 assert!(output[2] == ((v2 << 6) | v3));
8424 }
8425 }
8426 }
8427
8428 #[kani::proof]
8429 #[kani::unwind(3)]
8430 fn standard_validate_tail_unpadded_accepts_or_rejects_without_panic() {
8431 let input = kani::any::<[u8; 3]>();
8432 let len = usize::from(kani::any::<u8>() % 4);
8433 let result = validate_tail_unpadded::<Standard>(&input[..len]);
8434
8435 if result.is_ok() {
8436 assert!(len == 0 || len == 2 || len == 3);
8437 }
8438 }
8439
8440 #[kani::proof]
8441 #[kani::unwind(3)]
8442 fn standard_decode_two_byte_tail_returns_written_within_output() {
8443 let input = kani::any::<[u8; 2]>();
8444 let mut output = kani::any::<[u8; 1]>();
8445 let result = decode_tail_unpadded::<Standard>(&input, &mut output);
8446
8447 if let Ok(written) = result {
8448 assert!(written <= output.len());
8449 assert!(written == 1);
8450 }
8451 }
8452
8453 #[kani::proof]
8454 #[kani::unwind(3)]
8455 fn standard_decode_three_byte_tail_returns_written_within_output() {
8456 let input = kani::any::<[u8; 3]>();
8457 let mut output = kani::any::<[u8; 2]>();
8458 let result = decode_tail_unpadded::<Standard>(&input, &mut output);
8459
8460 if let Ok(written) = result {
8461 assert!(written <= output.len());
8462 assert!(written == 2);
8463 }
8464 }
8465
8466 #[kani::proof]
8467 #[kani::unwind(3)]
8468 fn standard_decode_slice_clear_tail_clears_output_on_error() {
8469 let input = kani::any::<[u8; 4]>();
8470 let mut output = kani::any::<[u8; 3]>();
8471 let result = STANDARD.decode_slice_clear_tail(&input, &mut output);
8472
8473 if result.is_err() {
8474 assert!(output.iter().all(|byte| *byte == 0));
8475 }
8476 }
8477
8478 #[kani::proof]
8479 #[kani::unwind(3)]
8480 fn standard_encode_slice_returns_written_within_output() {
8481 let input = kani::any::<[u8; 3]>();
8482 let mut output = kani::any::<[u8; 4]>();
8483 let result = STANDARD.encode_slice(&input, &mut output);
8484
8485 if let Ok(written) = result {
8486 assert!(written <= output.len());
8487 }
8488 }
8489
8490 #[kani::proof]
8491 #[kani::unwind(4)]
8492 fn standard_encode_in_place_returns_prefix_within_buffer() {
8493 let mut buffer = kani::any::<[u8; 8]>();
8494 let input_len = usize::from(kani::any::<u8>() % 9);
8495 let result = STANDARD.encode_in_place(&mut buffer, input_len);
8496
8497 if let Ok(encoded) = result {
8498 assert!(encoded.len() <= 8);
8499 }
8500 }
8501
8502 #[kani::proof]
8503 #[kani::unwind(3)]
8504 fn standard_clear_tail_decode_clears_buffer_on_error() {
8505 let mut buffer = kani::any::<[u8; 4]>();
8506 let result = STANDARD.decode_in_place_clear_tail(&mut buffer);
8507
8508 if result.is_err() {
8509 assert!(buffer.iter().all(|byte| *byte == 0));
8510 }
8511 }
8512
8513 #[kani::proof]
8514 #[kani::unwind(3)]
8515 fn ct_standard_decode_slice_returns_written_within_output() {
8516 let input = kani::any::<[u8; 4]>();
8517 let mut output = kani::any::<[u8; 3]>();
8518 let result = ct::STANDARD.decode_slice_clear_tail(&input, &mut output);
8519
8520 if let Ok(written) = result {
8521 assert!(written <= output.len());
8522 }
8523 }
8524
8525 #[kani::proof]
8526 #[kani::unwind(3)]
8527 fn ct_standard_decode_slice_clear_tail_clears_output_on_error() {
8528 let input = kani::any::<[u8; 4]>();
8529 let mut output = kani::any::<[u8; 3]>();
8530 let result = ct::STANDARD.decode_slice_clear_tail(&input, &mut output);
8531
8532 if result.is_err() {
8533 assert!(output.iter().all(|byte| *byte == 0));
8534 }
8535 }
8536
8537 #[kani::proof]
8538 #[kani::unwind(3)]
8539 fn ct_standard_decode_in_place_clear_tail_clears_buffer_on_error() {
8540 let mut buffer = kani::any::<[u8; 4]>();
8541 let result = ct::STANDARD.decode_in_place_clear_tail(&mut buffer);
8542
8543 if result.is_err() {
8544 assert!(buffer.iter().all(|byte| *byte == 0));
8545 }
8546 }
8547
8548 #[kani::proof]
8549 #[kani::unwind(3)]
8550 fn ct_standard_validate_matches_decode_for_one_quantum() {
8551 let input = kani::any::<[u8; 4]>();
8552 let mut output = kani::any::<[u8; 3]>();
8553
8554 let validate_ok = ct::STANDARD.validate_result(&input).is_ok();
8555 let decode_ok = ct::STANDARD
8556 .decode_slice_clear_tail(&input, &mut output)
8557 .is_ok();
8558
8559 assert!(validate_ok == decode_ok);
8560 }
8561}
8562
8563#[cfg(test)]
8564mod tests {
8565 use super::*;
8566
8567 fn fill_pattern(output: &mut [u8], seed: usize) {
8568 for (index, byte) in output.iter_mut().enumerate() {
8569 let value = (index * 73 + seed * 19) % 256;
8570 *byte = u8::try_from(value).unwrap();
8571 }
8572 }
8573
8574 fn assert_encode_backend_matches_scalar<A, const PAD: bool>(input: &[u8])
8575 where
8576 A: Alphabet,
8577 {
8578 let engine = Engine::<A, PAD>::new();
8579 let mut dispatched = [0x55; 256];
8580 let mut scalar = [0xaa; 256];
8581
8582 let dispatched_result = engine.encode_slice(input, &mut dispatched);
8583 let scalar_result = backend::scalar_reference_encode_slice::<A, PAD>(input, &mut scalar);
8584
8585 assert_eq!(dispatched_result, scalar_result);
8586 if let Ok(written) = dispatched_result {
8587 assert_eq!(&dispatched[..written], &scalar[..written]);
8588 }
8589
8590 let required = checked_encoded_len(input.len(), PAD).unwrap();
8591 if required > 0 {
8592 let mut dispatched_short = [0x55; 256];
8593 let mut scalar_short = [0xaa; 256];
8594 let available = required - 1;
8595
8596 assert_eq!(
8597 engine.encode_slice(input, &mut dispatched_short[..available]),
8598 backend::scalar_reference_encode_slice::<A, PAD>(
8599 input,
8600 &mut scalar_short[..available],
8601 )
8602 );
8603 }
8604 }
8605
8606 fn assert_decode_backend_matches_scalar<A, const PAD: bool>(input: &[u8])
8607 where
8608 A: Alphabet,
8609 {
8610 let engine = Engine::<A, PAD>::new();
8611 let mut dispatched = [0x55; 128];
8612 let mut scalar = [0xaa; 128];
8613
8614 let dispatched_result = engine.decode_slice(input, &mut dispatched);
8615 let scalar_result = backend::scalar_reference_decode_slice::<A, PAD>(input, &mut scalar);
8616
8617 assert_eq!(dispatched_result, scalar_result);
8618 if let Ok(written) = dispatched_result {
8619 assert_eq!(&dispatched[..written], &scalar[..written]);
8620
8621 if written > 0 {
8622 let mut dispatched_short = [0x55; 128];
8623 let mut scalar_short = [0xaa; 128];
8624 let available = written - 1;
8625
8626 assert_eq!(
8627 engine.decode_slice(input, &mut dispatched_short[..available]),
8628 backend::scalar_reference_decode_slice::<A, PAD>(
8629 input,
8630 &mut scalar_short[..available],
8631 )
8632 );
8633 }
8634 }
8635 }
8636
8637 fn assert_backend_round_trip_matches_scalar<A, const PAD: bool>(input: &[u8])
8638 where
8639 A: Alphabet,
8640 {
8641 assert_encode_backend_matches_scalar::<A, PAD>(input);
8642
8643 let mut encoded = [0; 256];
8644 let encoded_len =
8645 backend::scalar_reference_encode_slice::<A, PAD>(input, &mut encoded).unwrap();
8646 assert_decode_backend_matches_scalar::<A, PAD>(&encoded[..encoded_len]);
8647 }
8648
8649 fn assert_standard_decode_chunk_matches_input(input: &[u8]) {
8650 let mut encoded = [0u8; 4];
8651 let encoded_len = STANDARD.encode_slice(input, &mut encoded).unwrap();
8652 assert_eq!(encoded_len, 4);
8653
8654 let chunk = [encoded[0], encoded[1], encoded[2], encoded[3]];
8655 let mut decoded = [0u8; 3];
8656 let decoded_len = decode_chunk::<Standard, true>(chunk, &mut decoded).unwrap();
8657
8658 assert_eq!(decoded_len, input.len());
8659 assert_eq!(&decoded[..decoded_len], input);
8660 }
8661
8662 #[test]
8663 fn backend_dispatch_matches_scalar_reference_for_canonical_inputs() {
8664 let mut input = [0; 128];
8665
8666 for input_len in 0..=input.len() {
8667 fill_pattern(&mut input[..input_len], input_len);
8668 let input = &input[..input_len];
8669
8670 assert_backend_round_trip_matches_scalar::<Standard, true>(input);
8671 assert_backend_round_trip_matches_scalar::<Standard, false>(input);
8672 assert_backend_round_trip_matches_scalar::<UrlSafe, true>(input);
8673 assert_backend_round_trip_matches_scalar::<UrlSafe, false>(input);
8674 }
8675 }
8676
8677 #[test]
8678 fn backend_dispatch_matches_scalar_reference_for_malformed_inputs() {
8679 for input in [
8680 &b"Z"[..],
8681 b"====",
8682 b"AA=A",
8683 b"Zh==",
8684 b"Zm9=",
8685 b"Zm9v$g==",
8686 b"Zm9vZh==",
8687 ] {
8688 assert_decode_backend_matches_scalar::<Standard, true>(input);
8689 }
8690
8691 for input in [&b"Z"[..], b"AA=A", b"Zh", b"Zm9", b"Zm9vYg$"] {
8692 assert_decode_backend_matches_scalar::<Standard, false>(input);
8693 }
8694
8695 assert_decode_backend_matches_scalar::<UrlSafe, true>(b"AA+A");
8696 assert_decode_backend_matches_scalar::<UrlSafe, false>(b"AA/A");
8697 assert_decode_backend_matches_scalar::<Standard, true>(b"AA-A");
8698 assert_decode_backend_matches_scalar::<Standard, false>(b"AA_A");
8699 }
8700
8701 #[test]
8702 fn decode_chunk_bit_packing_matches_exhaustive_small_inputs() {
8703 for byte in u8::MIN..=u8::MAX {
8704 assert_standard_decode_chunk_matches_input(&[byte]);
8705 }
8706
8707 for first in u8::MIN..=u8::MAX {
8708 for second in u8::MIN..=u8::MAX {
8709 assert_standard_decode_chunk_matches_input(&[first, second]);
8710 }
8711 }
8712 }
8713
8714 #[test]
8715 fn decode_chunk_bit_packing_matches_representative_full_quanta() {
8716 const SAMPLES: [u8; 16] = [
8717 0, 1, 2, 15, 16, 31, 32, 63, 64, 95, 127, 128, 191, 192, 254, 255,
8718 ];
8719
8720 for first in SAMPLES {
8721 for second in SAMPLES {
8722 for third in SAMPLES {
8723 assert_standard_decode_chunk_matches_input(&[first, second, third]);
8724 }
8725 }
8726 }
8727 }
8728
8729 #[test]
8730 fn ct_padded_final_quantum_fails_closed_for_invalid_padding_count() {
8731 let (_, invalid_byte, invalid_padding, written) =
8732 ct_padded_final_quantum::<Standard>(*b"ABCD", 3);
8733
8734 assert_ne!(invalid_byte, 0);
8735 assert_ne!(invalid_padding, 0);
8736 assert_eq!(written, 0);
8737 assert_eq!(
8738 report_ct_error(invalid_byte, invalid_padding),
8739 Err(DecodeError::InvalidInput)
8740 );
8741 }
8742
8743 #[cfg(feature = "simd")]
8744 #[test]
8745 fn simd_dispatch_scaffold_keeps_scalar_active() {
8746 assert_eq!(simd::active_backend(), simd::ActiveBackend::Scalar);
8747 let _candidate = simd::detected_candidate();
8748 }
8749
8750 #[test]
8751 fn encodes_standard_vectors() {
8752 let vectors = [
8753 (&b""[..], &b""[..]),
8754 (&b"f"[..], &b"Zg=="[..]),
8755 (&b"fo"[..], &b"Zm8="[..]),
8756 (&b"foo"[..], &b"Zm9v"[..]),
8757 (&b"foob"[..], &b"Zm9vYg=="[..]),
8758 (&b"fooba"[..], &b"Zm9vYmE="[..]),
8759 (&b"foobar"[..], &b"Zm9vYmFy"[..]),
8760 ];
8761 for (input, expected) in vectors {
8762 let mut output = [0u8; 16];
8763 let written = STANDARD.encode_slice(input, &mut output).unwrap();
8764 assert_eq!(&output[..written], expected);
8765 }
8766 }
8767
8768 #[test]
8769 fn decodes_standard_vectors() {
8770 let vectors = [
8771 (&b""[..], &b""[..]),
8772 (&b"Zg=="[..], &b"f"[..]),
8773 (&b"Zm8="[..], &b"fo"[..]),
8774 (&b"Zm9v"[..], &b"foo"[..]),
8775 (&b"Zm9vYg=="[..], &b"foob"[..]),
8776 (&b"Zm9vYmE="[..], &b"fooba"[..]),
8777 (&b"Zm9vYmFy"[..], &b"foobar"[..]),
8778 ];
8779 for (input, expected) in vectors {
8780 let mut output = [0u8; 16];
8781 let written = STANDARD.decode_slice(input, &mut output).unwrap();
8782 assert_eq!(&output[..written], expected);
8783 }
8784 }
8785
8786 #[test]
8787 fn supports_unpadded_url_safe() {
8788 let mut encoded = [0u8; 16];
8789 let written = URL_SAFE_NO_PAD
8790 .encode_slice(b"\xfb\xff", &mut encoded)
8791 .unwrap();
8792 assert_eq!(&encoded[..written], b"-_8");
8793
8794 let mut decoded = [0u8; 2];
8795 let written = URL_SAFE_NO_PAD
8796 .decode_slice(&encoded[..written], &mut decoded)
8797 .unwrap();
8798 assert_eq!(&decoded[..written], b"\xfb\xff");
8799 }
8800
8801 #[test]
8802 fn decodes_in_place() {
8803 let mut buffer = *b"Zm9vYmFy";
8804 let decoded = STANDARD_NO_PAD.decode_in_place(&mut buffer).unwrap();
8805 assert_eq!(decoded, b"foobar");
8806 }
8807
8808 #[test]
8809 fn rejects_non_canonical_padding_bits() {
8810 let mut output = [0u8; 4];
8811 assert_eq!(
8812 STANDARD.decode_slice(b"Zh==", &mut output),
8813 Err(DecodeError::InvalidPadding { index: 1 })
8814 );
8815 assert_eq!(
8816 STANDARD.decode_slice(b"Zm9=", &mut output),
8817 Err(DecodeError::InvalidPadding { index: 2 })
8818 );
8819 }
8820}