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