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};
476 use std::io::{self, Read, Write};
477
478 struct OutputQueue<const CAP: usize> {
479 buffer: [u8; CAP],
480 start: usize,
481 len: usize,
482 }
483
484 impl<const CAP: usize> OutputQueue<CAP> {
485 const fn new() -> Self {
486 Self {
487 buffer: [0; CAP],
488 start: 0,
489 len: 0,
490 }
491 }
492
493 const fn is_empty(&self) -> bool {
494 self.len == 0
495 }
496
497 fn push_slice(&mut self, input: &[u8]) -> io::Result<()> {
498 if input.len() > self.available_capacity() {
499 return Err(io::Error::new(
500 io::ErrorKind::InvalidInput,
501 "base64 stream output queue capacity exceeded",
502 ));
503 }
504
505 let mut read = 0;
506 while read < input.len() {
507 let write = (self.start + self.len) % CAP;
508 self.buffer[write] = input[read];
509 self.len += 1;
510 read += 1;
511 }
512
513 Ok(())
514 }
515
516 fn pop_front(&mut self) -> Option<u8> {
517 if self.len == 0 {
518 return None;
519 }
520
521 let byte = self.buffer[self.start];
522 self.buffer[self.start] = 0;
523 self.start = (self.start + 1) % CAP;
524 self.len -= 1;
525 if self.len == 0 {
526 self.start = 0;
527 }
528 Some(byte)
529 }
530
531 fn clear_all(&mut self) {
532 crate::wipe_bytes(&mut self.buffer);
533 self.start = 0;
534 self.len = 0;
535 }
536
537 const fn available_capacity(&self) -> usize {
538 CAP - self.len
539 }
540 }
541
542 pub struct Encoder<W, A, const PAD: bool>
544 where
545 A: Alphabet,
546 {
547 inner: Option<W>,
548 engine: Engine<A, PAD>,
549 pending: [u8; 2],
550 pending_len: usize,
551 }
552
553 impl<W, A, const PAD: bool> Encoder<W, A, PAD>
554 where
555 A: Alphabet,
556 {
557 #[must_use]
559 pub const fn new(inner: W, engine: Engine<A, PAD>) -> Self {
560 Self {
561 inner: Some(inner),
562 engine,
563 pending: [0; 2],
564 pending_len: 0,
565 }
566 }
567
568 #[must_use]
570 pub fn get_ref(&self) -> &W {
571 self.inner_ref()
572 }
573
574 pub fn get_mut(&mut self) -> &mut W {
576 self.inner_mut()
577 }
578
579 #[must_use]
583 pub fn into_inner(mut self) -> W {
584 self.take_inner()
585 }
586
587 fn inner_ref(&self) -> &W {
588 match &self.inner {
589 Some(inner) => inner,
590 None => unreachable!("stream encoder inner writer was already taken"),
591 }
592 }
593
594 fn inner_mut(&mut self) -> &mut W {
595 match &mut self.inner {
596 Some(inner) => inner,
597 None => unreachable!("stream encoder inner writer was already taken"),
598 }
599 }
600
601 fn take_inner(&mut self) -> W {
602 match self.inner.take() {
603 Some(inner) => inner,
604 None => unreachable!("stream encoder inner writer was already taken"),
605 }
606 }
607
608 fn clear_pending(&mut self) {
609 crate::wipe_bytes(&mut self.pending);
610 self.pending_len = 0;
611 }
612 }
613
614 impl<W, A, const PAD: bool> Drop for Encoder<W, A, PAD>
615 where
616 A: Alphabet,
617 {
618 fn drop(&mut self) {
619 self.clear_pending();
620 }
621 }
622
623 impl<W, A, const PAD: bool> Encoder<W, A, PAD>
624 where
625 W: Write,
626 A: Alphabet,
627 {
628 pub fn finish(mut self) -> io::Result<W> {
630 self.write_pending_final()?;
631 self.inner_mut().flush()?;
632 Ok(self.take_inner())
633 }
634
635 fn write_pending_final(&mut self) -> io::Result<()> {
636 if self.pending_len == 0 {
637 return Ok(());
638 }
639
640 let mut pending = [0u8; 2];
641 pending[..self.pending_len].copy_from_slice(&self.pending[..self.pending_len]);
642 let pending_len = self.pending_len;
643 let mut encoded = [0u8; 4];
644 let result = self.write_encoded_temp(&pending[..pending_len], &mut encoded);
645 crate::wipe_bytes(&mut pending);
646 result?;
647 self.clear_pending();
648 Ok(())
649 }
650
651 fn write_encoded_temp(&mut self, input: &[u8], encoded: &mut [u8]) -> io::Result<()> {
652 let written = match self.engine.encode_slice(input, encoded) {
653 Ok(written) => written,
654 Err(err) => {
655 crate::wipe_bytes(encoded);
656 return Err(encode_error_to_io(err));
657 }
658 };
659
660 let result = self.inner_mut().write_all(&encoded[..written]);
661 crate::wipe_bytes(encoded);
662 result
663 }
664 }
665
666 impl<W, A, const PAD: bool> Write for Encoder<W, A, PAD>
667 where
668 W: Write,
669 A: Alphabet,
670 {
671 fn write(&mut self, input: &[u8]) -> io::Result<usize> {
672 if input.is_empty() {
673 return Ok(0);
674 }
675
676 let mut consumed = 0;
677 if self.pending_len > 0 {
678 let needed = 3 - self.pending_len;
679 if input.len() < needed {
680 self.pending[self.pending_len..self.pending_len + input.len()]
681 .copy_from_slice(input);
682 self.pending_len += input.len();
683 return Ok(input.len());
684 }
685
686 let mut chunk = [0u8; 3];
687 chunk[..self.pending_len].copy_from_slice(&self.pending[..self.pending_len]);
688 chunk[self.pending_len..].copy_from_slice(&input[..needed]);
689
690 let mut encoded = [0u8; 4];
691 let result = self.write_encoded_temp(&chunk, &mut encoded);
692 crate::wipe_bytes(&mut chunk);
693 result?;
694 self.clear_pending();
695 consumed += needed;
696 }
697
698 let remaining = &input[consumed..];
699 let full_len = remaining.len() / 3 * 3;
700 let mut offset = 0;
701 let mut encoded = [0u8; 1024];
702 while offset < full_len {
703 let mut take = core::cmp::min(full_len - offset, 768);
704 take -= take % 3;
705 debug_assert!(take > 0);
706
707 self.write_encoded_temp(&remaining[offset..offset + take], &mut encoded)?;
708 offset += take;
709 }
710
711 let tail = &remaining[full_len..];
712 self.pending[..tail.len()].copy_from_slice(tail);
713 self.pending_len = tail.len();
714
715 Ok(input.len())
716 }
717
718 fn flush(&mut self) -> io::Result<()> {
719 self.inner_mut().flush()
720 }
721 }
722
723 fn encode_error_to_io(err: EncodeError) -> io::Error {
724 io::Error::new(io::ErrorKind::InvalidInput, err)
725 }
726
727 pub struct Decoder<W, A, const PAD: bool>
729 where
730 A: Alphabet,
731 {
732 inner: Option<W>,
733 engine: Engine<A, PAD>,
734 pending: [u8; 4],
735 pending_len: usize,
736 finished: bool,
737 }
738
739 impl<W, A, const PAD: bool> Decoder<W, A, PAD>
740 where
741 A: Alphabet,
742 {
743 #[must_use]
745 pub const fn new(inner: W, engine: Engine<A, PAD>) -> Self {
746 Self {
747 inner: Some(inner),
748 engine,
749 pending: [0; 4],
750 pending_len: 0,
751 finished: false,
752 }
753 }
754
755 #[must_use]
757 pub fn get_ref(&self) -> &W {
758 self.inner_ref()
759 }
760
761 pub fn get_mut(&mut self) -> &mut W {
763 self.inner_mut()
764 }
765
766 #[must_use]
770 pub fn into_inner(mut self) -> W {
771 self.take_inner()
772 }
773
774 fn inner_ref(&self) -> &W {
775 match &self.inner {
776 Some(inner) => inner,
777 None => unreachable!("stream decoder inner writer was already taken"),
778 }
779 }
780
781 fn inner_mut(&mut self) -> &mut W {
782 match &mut self.inner {
783 Some(inner) => inner,
784 None => unreachable!("stream decoder inner writer was already taken"),
785 }
786 }
787
788 fn take_inner(&mut self) -> W {
789 match self.inner.take() {
790 Some(inner) => inner,
791 None => unreachable!("stream decoder inner writer was already taken"),
792 }
793 }
794
795 fn clear_pending(&mut self) {
796 crate::wipe_bytes(&mut self.pending);
797 self.pending_len = 0;
798 }
799 }
800
801 impl<W, A, const PAD: bool> Drop for Decoder<W, A, PAD>
802 where
803 A: Alphabet,
804 {
805 fn drop(&mut self) {
806 self.clear_pending();
807 }
808 }
809
810 impl<W, A, const PAD: bool> Decoder<W, A, PAD>
811 where
812 W: Write,
813 A: Alphabet,
814 {
815 pub fn finish(mut self) -> io::Result<W> {
817 self.write_pending_final()?;
818 self.inner_mut().flush()?;
819 Ok(self.take_inner())
820 }
821
822 fn write_pending_final(&mut self) -> io::Result<()> {
823 if self.pending_len == 0 {
824 return Ok(());
825 }
826
827 let mut pending = [0u8; 4];
828 pending[..self.pending_len].copy_from_slice(&self.pending[..self.pending_len]);
829 let pending_len = self.pending_len;
830 let mut decoded = [0u8; 3];
831 let result = self.write_decoded_temp(&pending[..pending_len], &mut decoded);
832 crate::wipe_bytes(&mut pending);
833 result?;
834 self.clear_pending();
835 Ok(())
836 }
837
838 fn write_full_quad(&mut self, mut input: [u8; 4]) -> io::Result<()> {
839 let mut decoded = [0u8; 3];
840 let result = self.write_decoded_temp(&input, &mut decoded);
841 crate::wipe_bytes(&mut input);
842 let written = result?;
843 if written < 3 {
844 self.finished = true;
845 }
846 Ok(())
847 }
848
849 fn write_decoded_temp(&mut self, input: &[u8], decoded: &mut [u8]) -> io::Result<usize> {
850 let written = match self.engine.decode_slice(input, decoded) {
851 Ok(written) => written,
852 Err(err) => {
853 crate::wipe_bytes(decoded);
854 return Err(decode_error_to_io(err));
855 }
856 };
857
858 let result = self.inner_mut().write_all(&decoded[..written]);
859 crate::wipe_bytes(decoded);
860 match result {
861 Ok(()) => Ok(written),
862 Err(err) => Err(err),
863 }
864 }
865 }
866
867 impl<W, A, const PAD: bool> Write for Decoder<W, A, PAD>
868 where
869 W: Write,
870 A: Alphabet,
871 {
872 fn write(&mut self, input: &[u8]) -> io::Result<usize> {
873 if input.is_empty() {
874 return Ok(0);
875 }
876 if self.finished {
877 return Err(trailing_input_after_padding_error());
878 }
879
880 let mut consumed = 0;
881 if self.pending_len > 0 {
882 let needed = 4 - self.pending_len;
883 if input.len() < needed {
884 self.pending[self.pending_len..self.pending_len + input.len()]
885 .copy_from_slice(input);
886 self.pending_len += input.len();
887 return Ok(input.len());
888 }
889
890 let mut quad = [0u8; 4];
891 quad[..self.pending_len].copy_from_slice(&self.pending[..self.pending_len]);
892 quad[self.pending_len..].copy_from_slice(&input[..needed]);
893 let result = self.write_full_quad(quad);
894 crate::wipe_bytes(&mut quad);
895 result?;
896 self.clear_pending();
897 consumed += needed;
898 if self.finished && consumed < input.len() {
899 return Err(trailing_input_after_padding_error());
900 }
901 }
902
903 let remaining = &input[consumed..];
904 let full_len = remaining.len() / 4 * 4;
905 let mut offset = 0;
906 while offset < full_len {
907 let quad = [
908 remaining[offset],
909 remaining[offset + 1],
910 remaining[offset + 2],
911 remaining[offset + 3],
912 ];
913 let mut quad = quad;
914 let result = self.write_full_quad(quad);
915 crate::wipe_bytes(&mut quad);
916 result?;
917 offset += 4;
918 if self.finished && offset < remaining.len() {
919 return Err(trailing_input_after_padding_error());
920 }
921 }
922
923 let tail = &remaining[full_len..];
924 self.pending[..tail.len()].copy_from_slice(tail);
925 self.pending_len = tail.len();
926
927 Ok(input.len())
928 }
929
930 fn flush(&mut self) -> io::Result<()> {
931 self.inner_mut().flush()
932 }
933 }
934
935 fn decode_error_to_io(err: DecodeError) -> io::Error {
936 io::Error::new(io::ErrorKind::InvalidInput, err)
937 }
938
939 fn trailing_input_after_padding_error() -> io::Error {
940 io::Error::new(
941 io::ErrorKind::InvalidInput,
942 "base64 decoder received trailing input after padding",
943 )
944 }
945
946 pub struct DecoderReader<R, A, const PAD: bool>
953 where
954 A: Alphabet,
955 {
956 inner: Option<R>,
957 engine: Engine<A, PAD>,
958 pending: [u8; 4],
959 pending_len: usize,
960 output: OutputQueue<3>,
961 finished: bool,
962 terminal_seen: bool,
963 }
964
965 impl<R, A, const PAD: bool> DecoderReader<R, A, PAD>
966 where
967 A: Alphabet,
968 {
969 #[must_use]
971 pub fn new(inner: R, engine: Engine<A, PAD>) -> Self {
972 Self {
973 inner: Some(inner),
974 engine,
975 pending: [0; 4],
976 pending_len: 0,
977 output: OutputQueue::new(),
978 finished: false,
979 terminal_seen: false,
980 }
981 }
982
983 #[must_use]
985 pub fn get_ref(&self) -> &R {
986 self.inner_ref()
987 }
988
989 pub fn get_mut(&mut self) -> &mut R {
991 self.inner_mut()
992 }
993
994 #[must_use]
996 pub fn into_inner(mut self) -> R {
997 self.take_inner()
998 }
999
1000 fn inner_ref(&self) -> &R {
1001 match &self.inner {
1002 Some(inner) => inner,
1003 None => unreachable!("stream decoder reader inner reader was already taken"),
1004 }
1005 }
1006
1007 fn inner_mut(&mut self) -> &mut R {
1008 match &mut self.inner {
1009 Some(inner) => inner,
1010 None => unreachable!("stream decoder reader inner reader was already taken"),
1011 }
1012 }
1013
1014 fn take_inner(&mut self) -> R {
1015 match self.inner.take() {
1016 Some(inner) => inner,
1017 None => unreachable!("stream decoder reader inner reader was already taken"),
1018 }
1019 }
1020
1021 fn clear_pending(&mut self) {
1022 crate::wipe_bytes(&mut self.pending);
1023 self.pending_len = 0;
1024 }
1025 }
1026
1027 impl<R, A, const PAD: bool> Drop for DecoderReader<R, A, PAD>
1028 where
1029 A: Alphabet,
1030 {
1031 fn drop(&mut self) {
1032 self.clear_pending();
1033 self.output.clear_all();
1034 }
1035 }
1036
1037 impl<R, A, const PAD: bool> Read for DecoderReader<R, A, PAD>
1038 where
1039 R: Read,
1040 A: Alphabet,
1041 {
1042 fn read(&mut self, output: &mut [u8]) -> io::Result<usize> {
1043 if output.is_empty() {
1044 return Ok(0);
1045 }
1046
1047 while self.output.is_empty() && !self.finished {
1048 self.fill_output()?;
1049 }
1050
1051 let mut written = 0;
1052 while written < output.len() {
1053 let Some(byte) = self.output.pop_front() else {
1054 break;
1055 };
1056 output[written] = byte;
1057 written += 1;
1058 }
1059
1060 Ok(written)
1061 }
1062 }
1063
1064 impl<R, A, const PAD: bool> DecoderReader<R, A, PAD>
1065 where
1066 R: Read,
1067 A: Alphabet,
1068 {
1069 fn fill_output(&mut self) -> io::Result<()> {
1070 if self.terminal_seen {
1071 self.finished = true;
1072 return Ok(());
1073 }
1074
1075 let mut input = [0u8; 4];
1076 let available = 4 - self.pending_len;
1077 let read = self.inner_mut().read(&mut input[..available])?;
1078 if read == 0 {
1079 crate::wipe_bytes(&mut input);
1080 self.finished = true;
1081 self.push_final_pending()?;
1082 return Ok(());
1083 }
1084
1085 self.pending[self.pending_len..self.pending_len + read].copy_from_slice(&input[..read]);
1086 crate::wipe_bytes(&mut input);
1087 self.pending_len += read;
1088 if self.pending_len < 4 {
1089 return Ok(());
1090 }
1091
1092 let mut quad = self.pending;
1093 self.clear_pending();
1094 let result = self.push_decoded(&quad);
1095 crate::wipe_bytes(&mut quad);
1096 result?;
1097 if self.terminal_seen {
1098 self.finished = true;
1099 }
1100 Ok(())
1101 }
1102
1103 fn push_final_pending(&mut self) -> io::Result<()> {
1104 if self.pending_len == 0 {
1105 return Ok(());
1106 }
1107
1108 let mut pending = [0u8; 4];
1109 pending[..self.pending_len].copy_from_slice(&self.pending[..self.pending_len]);
1110 let pending_len = self.pending_len;
1111 self.clear_pending();
1112 let result = self.push_decoded(&pending[..pending_len]);
1113 crate::wipe_bytes(&mut pending);
1114 result
1115 }
1116
1117 fn push_decoded(&mut self, input: &[u8]) -> io::Result<()> {
1118 let mut decoded = [0u8; 3];
1119 let written = match self.engine.decode_slice(input, &mut decoded) {
1120 Ok(written) => written,
1121 Err(err) => {
1122 crate::wipe_bytes(&mut decoded);
1123 return Err(decode_error_to_io(err));
1124 }
1125 };
1126 let result = self.output.push_slice(&decoded[..written]);
1127 crate::wipe_bytes(&mut decoded);
1128 result?;
1129 if input.len() == 4 && written < 3 {
1130 self.terminal_seen = true;
1131 }
1132 Ok(())
1133 }
1134 }
1135
1136 pub struct EncoderReader<R, A, const PAD: bool>
1138 where
1139 A: Alphabet,
1140 {
1141 inner: Option<R>,
1142 engine: Engine<A, PAD>,
1143 pending: [u8; 2],
1144 pending_len: usize,
1145 output: OutputQueue<1024>,
1146 finished: bool,
1147 }
1148
1149 impl<R, A, const PAD: bool> EncoderReader<R, A, PAD>
1150 where
1151 A: Alphabet,
1152 {
1153 #[must_use]
1155 pub fn new(inner: R, engine: Engine<A, PAD>) -> Self {
1156 Self {
1157 inner: Some(inner),
1158 engine,
1159 pending: [0; 2],
1160 pending_len: 0,
1161 output: OutputQueue::new(),
1162 finished: false,
1163 }
1164 }
1165
1166 #[must_use]
1168 pub fn get_ref(&self) -> &R {
1169 self.inner_ref()
1170 }
1171
1172 pub fn get_mut(&mut self) -> &mut R {
1174 self.inner_mut()
1175 }
1176
1177 #[must_use]
1179 pub fn into_inner(mut self) -> R {
1180 self.take_inner()
1181 }
1182
1183 fn inner_ref(&self) -> &R {
1184 match &self.inner {
1185 Some(inner) => inner,
1186 None => unreachable!("stream encoder reader inner reader was already taken"),
1187 }
1188 }
1189
1190 fn inner_mut(&mut self) -> &mut R {
1191 match &mut self.inner {
1192 Some(inner) => inner,
1193 None => unreachable!("stream encoder reader inner reader was already taken"),
1194 }
1195 }
1196
1197 fn take_inner(&mut self) -> R {
1198 match self.inner.take() {
1199 Some(inner) => inner,
1200 None => unreachable!("stream encoder reader inner reader was already taken"),
1201 }
1202 }
1203
1204 fn clear_pending(&mut self) {
1205 crate::wipe_bytes(&mut self.pending);
1206 self.pending_len = 0;
1207 }
1208 }
1209
1210 impl<R, A, const PAD: bool> Drop for EncoderReader<R, A, PAD>
1211 where
1212 A: Alphabet,
1213 {
1214 fn drop(&mut self) {
1215 self.clear_pending();
1216 self.output.clear_all();
1217 }
1218 }
1219
1220 impl<R, A, const PAD: bool> Read for EncoderReader<R, A, PAD>
1221 where
1222 R: Read,
1223 A: Alphabet,
1224 {
1225 fn read(&mut self, output: &mut [u8]) -> io::Result<usize> {
1226 if output.is_empty() {
1227 return Ok(0);
1228 }
1229
1230 while self.output.is_empty() && !self.finished {
1231 self.fill_output()?;
1232 }
1233
1234 let mut written = 0;
1235 while written < output.len() {
1236 let Some(byte) = self.output.pop_front() else {
1237 break;
1238 };
1239 output[written] = byte;
1240 written += 1;
1241 }
1242
1243 Ok(written)
1244 }
1245 }
1246
1247 impl<R, A, const PAD: bool> EncoderReader<R, A, PAD>
1248 where
1249 R: Read,
1250 A: Alphabet,
1251 {
1252 fn fill_output(&mut self) -> io::Result<()> {
1253 let mut input = [0u8; 768];
1254 let read = self.inner_mut().read(&mut input)?;
1255 if read == 0 {
1256 crate::wipe_bytes(&mut input);
1257 self.finished = true;
1258 self.push_final_pending()?;
1259 return Ok(());
1260 }
1261
1262 let mut consumed = 0;
1263 if self.pending_len > 0 {
1264 let needed = 3 - self.pending_len;
1265 if read < needed {
1266 self.pending[self.pending_len..self.pending_len + read]
1267 .copy_from_slice(&input[..read]);
1268 self.pending_len += read;
1269 crate::wipe_bytes(&mut input);
1270 return Ok(());
1271 }
1272
1273 let mut chunk = [0u8; 3];
1274 chunk[..self.pending_len].copy_from_slice(&self.pending[..self.pending_len]);
1275 chunk[self.pending_len..].copy_from_slice(&input[..needed]);
1276 let result = self.push_encoded(&chunk);
1277 crate::wipe_bytes(&mut chunk);
1278 if let Err(err) = result {
1279 crate::wipe_bytes(&mut input);
1280 return Err(err);
1281 }
1282 self.clear_pending();
1283 consumed += needed;
1284 }
1285
1286 let remaining = &input[consumed..read];
1287 let full_len = remaining.len() / 3 * 3;
1288 let tail_len = remaining.len() - full_len;
1289 let mut tail = [0u8; 2];
1290 tail[..tail_len].copy_from_slice(&remaining[full_len..]);
1291 let result = if full_len > 0 {
1292 self.push_encoded(&remaining[..full_len])
1293 } else {
1294 Ok(())
1295 };
1296 crate::wipe_bytes(&mut input);
1297 if let Err(err) = result {
1298 crate::wipe_bytes(&mut tail);
1299 return Err(err);
1300 }
1301 self.pending[..tail_len].copy_from_slice(&tail[..tail_len]);
1302 crate::wipe_bytes(&mut tail);
1303 self.pending_len = tail_len;
1304 Ok(())
1305 }
1306
1307 fn push_final_pending(&mut self) -> io::Result<()> {
1308 if self.pending_len == 0 {
1309 return Ok(());
1310 }
1311
1312 let mut pending = [0u8; 2];
1313 pending[..self.pending_len].copy_from_slice(&self.pending[..self.pending_len]);
1314 let pending_len = self.pending_len;
1315 self.clear_pending();
1316 let result = self.push_encoded(&pending[..pending_len]);
1317 crate::wipe_bytes(&mut pending);
1318 result
1319 }
1320
1321 fn push_encoded(&mut self, input: &[u8]) -> io::Result<()> {
1322 let mut encoded = [0u8; 1024];
1323 let written = match self.engine.encode_slice(input, &mut encoded) {
1324 Ok(written) => written,
1325 Err(err) => {
1326 crate::wipe_bytes(&mut encoded);
1327 return Err(encode_error_to_io(err));
1328 }
1329 };
1330 let result = self.output.push_slice(&encoded[..written]);
1331 crate::wipe_bytes(&mut encoded);
1332 result
1333 }
1334 }
1335}
1336
1337pub mod ct {
1345 use super::{
1346 Alphabet, DecodeError, DecodedBuffer, Standard, UrlSafe, ct_decode_in_place,
1347 ct_decode_slice, ct_validate_decode,
1348 };
1349 use core::marker::PhantomData;
1350
1351 pub const STANDARD: CtEngine<Standard, true> = CtEngine::new();
1353
1354 pub const STANDARD_NO_PAD: CtEngine<Standard, false> = CtEngine::new();
1356
1357 pub const URL_SAFE: CtEngine<UrlSafe, true> = CtEngine::new();
1359
1360 pub const URL_SAFE_NO_PAD: CtEngine<UrlSafe, false> = CtEngine::new();
1362
1363 #[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
1365 pub struct CtEngine<A, const PAD: bool> {
1366 alphabet: PhantomData<A>,
1367 }
1368
1369 impl<A, const PAD: bool> CtEngine<A, PAD>
1370 where
1371 A: Alphabet,
1372 {
1373 #[must_use]
1375 pub const fn new() -> Self {
1376 Self {
1377 alphabet: PhantomData,
1378 }
1379 }
1380
1381 #[must_use]
1384 pub const fn is_padded(&self) -> bool {
1385 PAD
1386 }
1387
1388 pub fn validate_result(&self, input: &[u8]) -> Result<(), DecodeError> {
1403 ct_validate_decode::<A, PAD>(input)
1404 }
1405
1406 #[must_use]
1420 pub fn validate(&self, input: &[u8]) -> bool {
1421 self.validate_result(input).is_ok()
1422 }
1423
1424 pub fn decode_slice(&self, input: &[u8], output: &mut [u8]) -> Result<usize, DecodeError> {
1446 ct_decode_slice::<A, PAD>(input, output)
1447 }
1448
1449 pub fn decode_slice_clear_tail(
1471 &self,
1472 input: &[u8],
1473 output: &mut [u8],
1474 ) -> Result<usize, DecodeError> {
1475 let written = match self.decode_slice(input, output) {
1476 Ok(written) => written,
1477 Err(err) => {
1478 crate::wipe_bytes(output);
1479 return Err(err);
1480 }
1481 };
1482 crate::wipe_tail(output, written);
1483 Ok(written)
1484 }
1485
1486 pub fn decode_buffer<const CAP: usize>(
1502 &self,
1503 input: &[u8],
1504 ) -> Result<DecodedBuffer<CAP>, DecodeError> {
1505 let mut output = DecodedBuffer::new();
1506 let written = match self.decode_slice_clear_tail(input, &mut output.bytes) {
1507 Ok(written) => written,
1508 Err(err) => {
1509 output.clear();
1510 return Err(err);
1511 }
1512 };
1513 output.len = written;
1514 Ok(output)
1515 }
1516
1517 pub fn decode_in_place<'a>(
1534 &self,
1535 buffer: &'a mut [u8],
1536 ) -> Result<&'a mut [u8], DecodeError> {
1537 let len = ct_decode_in_place::<A, PAD>(buffer)?;
1538 Ok(&mut buffer[..len])
1539 }
1540
1541 pub fn decode_in_place_clear_tail<'a>(
1558 &self,
1559 buffer: &'a mut [u8],
1560 ) -> Result<&'a mut [u8], DecodeError> {
1561 let len = match ct_decode_in_place::<A, PAD>(buffer) {
1562 Ok(len) => len,
1563 Err(err) => {
1564 crate::wipe_bytes(buffer);
1565 return Err(err);
1566 }
1567 };
1568 crate::wipe_tail(buffer, len);
1569 Ok(&mut buffer[..len])
1570 }
1571 }
1572}
1573
1574pub const STANDARD: Engine<Standard, true> = Engine::new();
1576
1577pub const STANDARD_NO_PAD: Engine<Standard, false> = Engine::new();
1579
1580pub const URL_SAFE: Engine<UrlSafe, true> = Engine::new();
1582
1583pub const URL_SAFE_NO_PAD: Engine<UrlSafe, false> = Engine::new();
1585
1586pub const BCRYPT_NO_PAD: Engine<Bcrypt, false> = Engine::new();
1591
1592pub const CRYPT_NO_PAD: Engine<Crypt, false> = Engine::new();
1597
1598#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1600pub enum LineEnding {
1601 Lf,
1603 CrLf,
1605}
1606
1607impl LineEnding {
1608 #[must_use]
1610 pub const fn as_bytes(self) -> &'static [u8] {
1611 match self {
1612 Self::Lf => b"\n",
1613 Self::CrLf => b"\r\n",
1614 }
1615 }
1616
1617 #[must_use]
1619 pub const fn byte_len(self) -> usize {
1620 match self {
1621 Self::Lf => 1,
1622 Self::CrLf => 2,
1623 }
1624 }
1625}
1626
1627#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1633pub struct LineWrap {
1634 pub line_len: usize,
1636 pub line_ending: LineEnding,
1638}
1639
1640impl LineWrap {
1641 pub const MIME: Self = Self::new(76, LineEnding::CrLf);
1643 pub const PEM: Self = Self::new(64, LineEnding::Lf);
1645 pub const PEM_CRLF: Self = Self::new(64, LineEnding::CrLf);
1647
1648 #[must_use]
1650 pub const fn new(line_len: usize, line_ending: LineEnding) -> Self {
1651 Self {
1652 line_len,
1653 line_ending,
1654 }
1655 }
1656
1657 #[must_use]
1664 pub const fn checked_new(line_len: usize, line_ending: LineEnding) -> Option<Self> {
1665 if line_len == 0 {
1666 None
1667 } else {
1668 Some(Self::new(line_len, line_ending))
1669 }
1670 }
1671
1672 #[must_use]
1674 pub const fn is_valid(self) -> bool {
1675 self.line_len != 0
1676 }
1677}
1678
1679#[allow(unsafe_code)]
1680fn wipe_bytes(bytes: &mut [u8]) {
1681 for byte in bytes {
1682 unsafe {
1686 core::ptr::write_volatile(byte, 0);
1687 }
1688 }
1689 core::sync::atomic::compiler_fence(core::sync::atomic::Ordering::SeqCst);
1690}
1691
1692fn wipe_tail(bytes: &mut [u8], start: usize) {
1693 wipe_bytes(&mut bytes[start..]);
1694}
1695
1696#[cfg(feature = "alloc")]
1697#[allow(unsafe_code)]
1698fn wipe_vec_spare_capacity(bytes: &mut alloc::vec::Vec<u8>) {
1699 let ptr = bytes.as_mut_ptr();
1700 let mut offset = bytes.len();
1701 while offset < bytes.capacity() {
1702 unsafe {
1706 core::ptr::write_volatile(ptr.add(offset), 0);
1707 }
1708 offset += 1;
1709 }
1710 core::sync::atomic::compiler_fence(core::sync::atomic::Ordering::SeqCst);
1711}
1712
1713#[cfg(feature = "alloc")]
1714fn wipe_vec_all(bytes: &mut alloc::vec::Vec<u8>) {
1715 wipe_bytes(bytes);
1716 wipe_vec_spare_capacity(bytes);
1717}
1718
1719pub struct EncodedBuffer<const CAP: usize> {
1729 bytes: [u8; CAP],
1730 len: usize,
1731}
1732
1733impl<const CAP: usize> EncodedBuffer<CAP> {
1734 #[must_use]
1736 pub const fn new() -> Self {
1737 Self {
1738 bytes: [0u8; CAP],
1739 len: 0,
1740 }
1741 }
1742
1743 #[must_use]
1745 pub const fn len(&self) -> usize {
1746 self.len
1747 }
1748
1749 #[must_use]
1751 pub const fn is_empty(&self) -> bool {
1752 self.len == 0
1753 }
1754
1755 #[must_use]
1757 pub const fn is_full(&self) -> bool {
1758 self.len == CAP
1759 }
1760
1761 #[must_use]
1763 pub const fn capacity(&self) -> usize {
1764 CAP
1765 }
1766
1767 #[must_use]
1769 pub const fn remaining_capacity(&self) -> usize {
1770 CAP - self.len
1771 }
1772
1773 #[must_use]
1775 pub fn as_bytes(&self) -> &[u8] {
1776 &self.bytes[..self.len]
1777 }
1778
1779 #[must_use]
1786 pub fn as_str(&self) -> &str {
1787 match core::str::from_utf8(self.as_bytes()) {
1788 Ok(output) => output,
1789 Err(_) => unreachable!("base64 encoder produced non-UTF-8 output"),
1790 }
1791 }
1792
1793 #[must_use]
1801 pub fn constant_time_eq(&self, other: &[u8]) -> bool {
1802 constant_time_eq_public_len(self.as_bytes(), other)
1803 }
1804
1805 #[must_use]
1813 pub fn into_exposed_array(mut self) -> ([u8; CAP], usize) {
1814 let len = self.len;
1815 self.len = 0;
1816 (core::mem::replace(&mut self.bytes, [0u8; CAP]), len)
1817 }
1818
1819 pub fn clear(&mut self) {
1821 wipe_bytes(&mut self.bytes);
1822 self.len = 0;
1823 }
1824
1825 pub fn clear_tail(&mut self) {
1827 wipe_tail(&mut self.bytes, self.len);
1828 }
1829}
1830
1831impl<const CAP: usize> AsRef<[u8]> for EncodedBuffer<CAP> {
1832 fn as_ref(&self) -> &[u8] {
1833 self.as_bytes()
1834 }
1835}
1836
1837impl<const CAP: usize> Clone for EncodedBuffer<CAP> {
1838 fn clone(&self) -> Self {
1839 let mut output = Self::new();
1840 output.bytes[..self.len].copy_from_slice(self.as_bytes());
1841 output.len = self.len;
1842 output
1843 }
1844}
1845
1846impl<const CAP: usize> core::fmt::Debug for EncodedBuffer<CAP> {
1847 fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
1848 formatter
1849 .debug_struct("EncodedBuffer")
1850 .field("bytes", &"<redacted>")
1851 .field("len", &self.len)
1852 .field("capacity", &CAP)
1853 .finish()
1854 }
1855}
1856
1857impl<const CAP: usize> Default for EncodedBuffer<CAP> {
1858 fn default() -> Self {
1859 Self::new()
1860 }
1861}
1862
1863impl<const CAP: usize> Drop for EncodedBuffer<CAP> {
1864 fn drop(&mut self) {
1865 self.clear();
1866 }
1867}
1868
1869impl<const CAP: usize> Eq for EncodedBuffer<CAP> {}
1870
1871impl<const CAP: usize> PartialEq for EncodedBuffer<CAP> {
1872 fn eq(&self, other: &Self) -> bool {
1873 self.constant_time_eq(other.as_bytes())
1874 }
1875}
1876
1877impl<const CAP: usize> PartialEq<&[u8]> for EncodedBuffer<CAP> {
1878 fn eq(&self, other: &&[u8]) -> bool {
1879 self.constant_time_eq(other)
1880 }
1881}
1882
1883impl<const CAP: usize, const N: usize> PartialEq<&[u8; N]> for EncodedBuffer<CAP> {
1884 fn eq(&self, other: &&[u8; N]) -> bool {
1885 self.constant_time_eq(&other[..])
1886 }
1887}
1888
1889impl<const CAP: usize> PartialEq<&str> for EncodedBuffer<CAP> {
1890 fn eq(&self, other: &&str) -> bool {
1891 self.constant_time_eq(other.as_bytes())
1892 }
1893}
1894
1895#[cfg(feature = "alloc")]
1896impl<const CAP: usize> PartialEq<alloc::string::String> for EncodedBuffer<CAP> {
1897 fn eq(&self, other: &alloc::string::String) -> bool {
1898 self.constant_time_eq(other.as_bytes())
1899 }
1900}
1901
1902impl<const CAP: usize> PartialEq<EncodedBuffer<CAP>> for &[u8] {
1903 fn eq(&self, other: &EncodedBuffer<CAP>) -> bool {
1904 other.constant_time_eq(self)
1905 }
1906}
1907
1908impl<const CAP: usize, const N: usize> PartialEq<EncodedBuffer<CAP>> for &[u8; N] {
1909 fn eq(&self, other: &EncodedBuffer<CAP>) -> bool {
1910 other.constant_time_eq(&self[..])
1911 }
1912}
1913
1914impl<const CAP: usize> PartialEq<EncodedBuffer<CAP>> for &str {
1915 fn eq(&self, other: &EncodedBuffer<CAP>) -> bool {
1916 other.constant_time_eq(self.as_bytes())
1917 }
1918}
1919
1920#[cfg(feature = "alloc")]
1921impl<const CAP: usize> PartialEq<EncodedBuffer<CAP>> for alloc::string::String {
1922 fn eq(&self, other: &EncodedBuffer<CAP>) -> bool {
1923 other.constant_time_eq(self.as_bytes())
1924 }
1925}
1926
1927impl<const CAP: usize> TryFrom<&[u8]> for EncodedBuffer<CAP> {
1928 type Error = EncodeError;
1929
1930 fn try_from(input: &[u8]) -> Result<Self, Self::Error> {
1936 STANDARD.encode_buffer(input)
1937 }
1938}
1939
1940impl<const CAP: usize> TryFrom<&str> for EncodedBuffer<CAP> {
1941 type Error = EncodeError;
1942
1943 fn try_from(input: &str) -> Result<Self, Self::Error> {
1950 Self::try_from(input.as_bytes())
1951 }
1952}
1953
1954pub struct DecodedBuffer<const CAP: usize> {
1964 bytes: [u8; CAP],
1965 len: usize,
1966}
1967
1968impl<const CAP: usize> DecodedBuffer<CAP> {
1969 #[must_use]
1971 pub const fn new() -> Self {
1972 Self {
1973 bytes: [0u8; CAP],
1974 len: 0,
1975 }
1976 }
1977
1978 #[must_use]
1980 pub const fn len(&self) -> usize {
1981 self.len
1982 }
1983
1984 #[must_use]
1986 pub const fn is_empty(&self) -> bool {
1987 self.len == 0
1988 }
1989
1990 #[must_use]
1992 pub const fn is_full(&self) -> bool {
1993 self.len == CAP
1994 }
1995
1996 #[must_use]
1998 pub const fn capacity(&self) -> usize {
1999 CAP
2000 }
2001
2002 #[must_use]
2004 pub const fn remaining_capacity(&self) -> usize {
2005 CAP - self.len
2006 }
2007
2008 #[must_use]
2010 pub fn as_bytes(&self) -> &[u8] {
2011 &self.bytes[..self.len]
2012 }
2013
2014 pub fn as_utf8(&self) -> Result<&str, core::str::Utf8Error> {
2020 core::str::from_utf8(self.as_bytes())
2021 }
2022
2023 #[must_use]
2031 pub fn constant_time_eq(&self, other: &[u8]) -> bool {
2032 constant_time_eq_public_len(self.as_bytes(), other)
2033 }
2034
2035 #[must_use]
2043 pub fn into_exposed_array(mut self) -> ([u8; CAP], usize) {
2044 let len = self.len;
2045 self.len = 0;
2046 (core::mem::replace(&mut self.bytes, [0u8; CAP]), len)
2047 }
2048
2049 pub fn clear(&mut self) {
2051 wipe_bytes(&mut self.bytes);
2052 self.len = 0;
2053 }
2054
2055 pub fn clear_tail(&mut self) {
2057 wipe_tail(&mut self.bytes, self.len);
2058 }
2059}
2060
2061impl<const CAP: usize> AsRef<[u8]> for DecodedBuffer<CAP> {
2062 fn as_ref(&self) -> &[u8] {
2063 self.as_bytes()
2064 }
2065}
2066
2067impl<const CAP: usize> Clone for DecodedBuffer<CAP> {
2068 fn clone(&self) -> Self {
2069 let mut output = Self::new();
2070 output.bytes[..self.len].copy_from_slice(self.as_bytes());
2071 output.len = self.len;
2072 output
2073 }
2074}
2075
2076impl<const CAP: usize> core::fmt::Debug for DecodedBuffer<CAP> {
2077 fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
2078 formatter
2079 .debug_struct("DecodedBuffer")
2080 .field("bytes", &"<redacted>")
2081 .field("len", &self.len)
2082 .field("capacity", &CAP)
2083 .finish()
2084 }
2085}
2086
2087impl<const CAP: usize> Default for DecodedBuffer<CAP> {
2088 fn default() -> Self {
2089 Self::new()
2090 }
2091}
2092
2093impl<const CAP: usize> Drop for DecodedBuffer<CAP> {
2094 fn drop(&mut self) {
2095 self.clear();
2096 }
2097}
2098
2099impl<const CAP: usize> Eq for DecodedBuffer<CAP> {}
2100
2101impl<const CAP: usize> PartialEq for DecodedBuffer<CAP> {
2102 fn eq(&self, other: &Self) -> bool {
2103 self.constant_time_eq(other.as_bytes())
2104 }
2105}
2106
2107impl<const CAP: usize> PartialEq<&[u8]> for DecodedBuffer<CAP> {
2108 fn eq(&self, other: &&[u8]) -> bool {
2109 self.constant_time_eq(other)
2110 }
2111}
2112
2113impl<const CAP: usize, const N: usize> PartialEq<&[u8; N]> for DecodedBuffer<CAP> {
2114 fn eq(&self, other: &&[u8; N]) -> bool {
2115 self.constant_time_eq(&other[..])
2116 }
2117}
2118
2119impl<const CAP: usize> PartialEq<&str> for DecodedBuffer<CAP> {
2120 fn eq(&self, other: &&str) -> bool {
2121 self.constant_time_eq(other.as_bytes())
2122 }
2123}
2124
2125#[cfg(feature = "alloc")]
2126impl<const CAP: usize> PartialEq<alloc::string::String> for DecodedBuffer<CAP> {
2127 fn eq(&self, other: &alloc::string::String) -> bool {
2128 self.constant_time_eq(other.as_bytes())
2129 }
2130}
2131
2132impl<const CAP: usize> PartialEq<DecodedBuffer<CAP>> for &[u8] {
2133 fn eq(&self, other: &DecodedBuffer<CAP>) -> bool {
2134 other.constant_time_eq(self)
2135 }
2136}
2137
2138impl<const CAP: usize, const N: usize> PartialEq<DecodedBuffer<CAP>> for &[u8; N] {
2139 fn eq(&self, other: &DecodedBuffer<CAP>) -> bool {
2140 other.constant_time_eq(&self[..])
2141 }
2142}
2143
2144impl<const CAP: usize> PartialEq<DecodedBuffer<CAP>> for &str {
2145 fn eq(&self, other: &DecodedBuffer<CAP>) -> bool {
2146 other.constant_time_eq(self.as_bytes())
2147 }
2148}
2149
2150#[cfg(feature = "alloc")]
2151impl<const CAP: usize> PartialEq<DecodedBuffer<CAP>> for alloc::string::String {
2152 fn eq(&self, other: &DecodedBuffer<CAP>) -> bool {
2153 other.constant_time_eq(self.as_bytes())
2154 }
2155}
2156
2157impl<const CAP: usize> TryFrom<&[u8]> for DecodedBuffer<CAP> {
2158 type Error = DecodeError;
2159
2160 fn try_from(input: &[u8]) -> Result<Self, Self::Error> {
2165 STANDARD.decode_buffer(input)
2166 }
2167}
2168
2169impl<const CAP: usize> TryFrom<&str> for DecodedBuffer<CAP> {
2170 type Error = DecodeError;
2171
2172 fn try_from(input: &str) -> Result<Self, Self::Error> {
2177 Self::try_from(input.as_bytes())
2178 }
2179}
2180
2181#[cfg(feature = "alloc")]
2193pub struct SecretBuffer {
2194 bytes: alloc::vec::Vec<u8>,
2195}
2196
2197#[cfg(feature = "alloc")]
2198impl SecretBuffer {
2199 #[must_use]
2201 pub fn from_vec(mut bytes: alloc::vec::Vec<u8>) -> Self {
2202 wipe_vec_spare_capacity(&mut bytes);
2203 Self { bytes }
2204 }
2205
2206 #[must_use]
2208 pub fn from_slice(bytes: &[u8]) -> Self {
2209 Self::from_vec(bytes.to_vec())
2210 }
2211
2212 #[must_use]
2214 pub fn len(&self) -> usize {
2215 self.bytes.len()
2216 }
2217
2218 #[must_use]
2220 pub fn is_empty(&self) -> bool {
2221 self.bytes.is_empty()
2222 }
2223
2224 #[must_use]
2229 pub fn expose_secret(&self) -> &[u8] {
2230 &self.bytes
2231 }
2232
2233 pub fn expose_secret_utf8(&self) -> Result<&str, core::str::Utf8Error> {
2239 core::str::from_utf8(self.expose_secret())
2240 }
2241
2242 #[must_use]
2247 pub fn expose_secret_mut(&mut self) -> &mut [u8] {
2248 &mut self.bytes
2249 }
2250
2251 #[must_use]
2258 pub fn into_exposed_vec(mut self) -> alloc::vec::Vec<u8> {
2259 core::mem::take(&mut self.bytes)
2260 }
2261
2262 pub fn try_into_exposed_string(self) -> Result<alloc::string::String, Self> {
2272 if core::str::from_utf8(self.expose_secret()).is_err() {
2273 return Err(self);
2274 }
2275
2276 match alloc::string::String::from_utf8(self.into_exposed_vec()) {
2277 Ok(text) => Ok(text),
2278 Err(error) => Err(Self::from_vec(error.into_bytes())),
2279 }
2280 }
2281
2282 #[must_use]
2290 pub fn constant_time_eq(&self, other: &[u8]) -> bool {
2291 constant_time_eq_public_len(self.expose_secret(), other)
2292 }
2293
2294 pub fn clear(&mut self) {
2296 wipe_vec_all(&mut self.bytes);
2297 self.bytes.clear();
2298 }
2299}
2300
2301#[cfg(feature = "alloc")]
2302impl Clone for SecretBuffer {
2303 fn clone(&self) -> Self {
2304 Self::from_slice(self.expose_secret())
2305 }
2306}
2307
2308#[cfg(feature = "alloc")]
2309impl core::fmt::Debug for SecretBuffer {
2310 fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
2311 formatter
2312 .debug_struct("SecretBuffer")
2313 .field("bytes", &"<redacted>")
2314 .field("len", &self.len())
2315 .finish()
2316 }
2317}
2318
2319#[cfg(feature = "alloc")]
2320impl core::fmt::Display for SecretBuffer {
2321 fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
2322 formatter.write_str("<redacted>")
2323 }
2324}
2325
2326#[cfg(feature = "alloc")]
2327impl Drop for SecretBuffer {
2328 fn drop(&mut self) {
2329 wipe_vec_all(&mut self.bytes);
2330 }
2331}
2332
2333#[cfg(feature = "alloc")]
2334impl Eq for SecretBuffer {}
2335
2336#[cfg(feature = "alloc")]
2337impl PartialEq for SecretBuffer {
2338 fn eq(&self, other: &Self) -> bool {
2339 self.constant_time_eq(other.expose_secret())
2340 }
2341}
2342
2343#[cfg(feature = "alloc")]
2344impl PartialEq<&[u8]> for SecretBuffer {
2345 fn eq(&self, other: &&[u8]) -> bool {
2346 self.constant_time_eq(other)
2347 }
2348}
2349
2350#[cfg(feature = "alloc")]
2351impl<const N: usize> PartialEq<&[u8; N]> for SecretBuffer {
2352 fn eq(&self, other: &&[u8; N]) -> bool {
2353 self.constant_time_eq(&other[..])
2354 }
2355}
2356
2357#[cfg(feature = "alloc")]
2358impl PartialEq<&str> for SecretBuffer {
2359 fn eq(&self, other: &&str) -> bool {
2360 self.constant_time_eq(other.as_bytes())
2361 }
2362}
2363
2364#[cfg(feature = "alloc")]
2365impl PartialEq<alloc::string::String> for SecretBuffer {
2366 fn eq(&self, other: &alloc::string::String) -> bool {
2367 self.constant_time_eq(other.as_bytes())
2368 }
2369}
2370
2371#[cfg(feature = "alloc")]
2372impl PartialEq<SecretBuffer> for &[u8] {
2373 fn eq(&self, other: &SecretBuffer) -> bool {
2374 other.constant_time_eq(self)
2375 }
2376}
2377
2378#[cfg(feature = "alloc")]
2379impl<const N: usize> PartialEq<SecretBuffer> for &[u8; N] {
2380 fn eq(&self, other: &SecretBuffer) -> bool {
2381 other.constant_time_eq(&self[..])
2382 }
2383}
2384
2385#[cfg(feature = "alloc")]
2386impl PartialEq<SecretBuffer> for &str {
2387 fn eq(&self, other: &SecretBuffer) -> bool {
2388 other.constant_time_eq(self.as_bytes())
2389 }
2390}
2391
2392#[cfg(feature = "alloc")]
2393impl PartialEq<SecretBuffer> for alloc::string::String {
2394 fn eq(&self, other: &SecretBuffer) -> bool {
2395 other.constant_time_eq(self.as_bytes())
2396 }
2397}
2398
2399#[cfg(feature = "alloc")]
2400impl From<alloc::vec::Vec<u8>> for SecretBuffer {
2401 fn from(bytes: alloc::vec::Vec<u8>) -> Self {
2406 Self::from_vec(bytes)
2407 }
2408}
2409
2410#[cfg(feature = "alloc")]
2411impl From<alloc::string::String> for SecretBuffer {
2412 fn from(text: alloc::string::String) -> Self {
2417 Self::from_vec(text.into_bytes())
2418 }
2419}
2420
2421#[cfg(feature = "alloc")]
2422impl<const CAP: usize> From<EncodedBuffer<CAP>> for SecretBuffer {
2423 fn from(buffer: EncodedBuffer<CAP>) -> Self {
2429 Self::from_slice(buffer.as_bytes())
2430 }
2431}
2432
2433#[cfg(feature = "alloc")]
2434impl<const CAP: usize> From<DecodedBuffer<CAP>> for SecretBuffer {
2435 fn from(buffer: DecodedBuffer<CAP>) -> Self {
2441 Self::from_slice(buffer.as_bytes())
2442 }
2443}
2444
2445#[cfg(feature = "alloc")]
2446impl TryFrom<&[u8]> for SecretBuffer {
2447 type Error = DecodeError;
2448
2449 fn try_from(input: &[u8]) -> Result<Self, Self::Error> {
2454 STANDARD.decode_secret(input)
2455 }
2456}
2457
2458#[cfg(feature = "alloc")]
2459impl TryFrom<&str> for SecretBuffer {
2460 type Error = DecodeError;
2461
2462 fn try_from(input: &str) -> Result<Self, Self::Error> {
2467 Self::try_from(input.as_bytes())
2468 }
2469}
2470
2471#[derive(Clone, Copy, Debug, Eq, PartialEq)]
2477pub struct Profile<A, const PAD: bool> {
2478 engine: Engine<A, PAD>,
2479 wrap: Option<LineWrap>,
2480}
2481
2482impl<A, const PAD: bool> Profile<A, PAD>
2483where
2484 A: Alphabet,
2485{
2486 #[must_use]
2488 pub const fn new(engine: Engine<A, PAD>, wrap: Option<LineWrap>) -> Self {
2489 Self { engine, wrap }
2490 }
2491
2492 #[must_use]
2498 pub const fn checked_new(engine: Engine<A, PAD>, wrap: Option<LineWrap>) -> Option<Self> {
2499 match wrap {
2500 Some(wrap) if !wrap.is_valid() => None,
2501 _ => Some(Self::new(engine, wrap)),
2502 }
2503 }
2504
2505 #[must_use]
2507 pub const fn is_valid(&self) -> bool {
2508 match self.wrap {
2509 Some(wrap) => wrap.is_valid(),
2510 None => true,
2511 }
2512 }
2513
2514 #[must_use]
2516 pub const fn engine(&self) -> Engine<A, PAD> {
2517 self.engine
2518 }
2519
2520 #[must_use]
2522 pub const fn is_padded(&self) -> bool {
2523 PAD
2524 }
2525
2526 #[must_use]
2528 pub const fn is_wrapped(&self) -> bool {
2529 self.wrap.is_some()
2530 }
2531
2532 #[must_use]
2534 pub const fn line_wrap(&self) -> Option<LineWrap> {
2535 self.wrap
2536 }
2537
2538 pub const fn encoded_len(&self, input_len: usize) -> Result<usize, EncodeError> {
2540 match self.wrap {
2541 Some(wrap) => wrapped_encoded_len(input_len, PAD, wrap),
2542 None => encoded_len(input_len, PAD),
2543 }
2544 }
2545
2546 #[must_use]
2549 pub const fn checked_encoded_len(&self, input_len: usize) -> Option<usize> {
2550 match self.wrap {
2551 Some(wrap) => checked_wrapped_encoded_len(input_len, PAD, wrap),
2552 None => checked_encoded_len(input_len, PAD),
2553 }
2554 }
2555
2556 pub fn decoded_len(&self, input: &[u8]) -> Result<usize, DecodeError> {
2558 match self.wrap {
2559 Some(wrap) => self.engine.decoded_len_wrapped(input, wrap),
2560 None => self.engine.decoded_len(input),
2561 }
2562 }
2563
2564 pub fn validate_result(&self, input: &[u8]) -> Result<(), DecodeError> {
2566 match self.wrap {
2567 Some(wrap) => self.engine.validate_wrapped_result(input, wrap),
2568 None => self.engine.validate_result(input),
2569 }
2570 }
2571
2572 #[must_use]
2574 pub fn validate(&self, input: &[u8]) -> bool {
2575 self.validate_result(input).is_ok()
2576 }
2577
2578 pub fn encode_slice(&self, input: &[u8], output: &mut [u8]) -> Result<usize, EncodeError> {
2580 match self.wrap {
2581 Some(wrap) => self.engine.encode_slice_wrapped(input, output, wrap),
2582 None => self.engine.encode_slice(input, output),
2583 }
2584 }
2585
2586 pub fn encode_slice_clear_tail(
2589 &self,
2590 input: &[u8],
2591 output: &mut [u8],
2592 ) -> Result<usize, EncodeError> {
2593 match self.wrap {
2594 Some(wrap) => self
2595 .engine
2596 .encode_slice_wrapped_clear_tail(input, output, wrap),
2597 None => self.engine.encode_slice_clear_tail(input, output),
2598 }
2599 }
2600
2601 pub fn encode_buffer<const CAP: usize>(
2607 &self,
2608 input: &[u8],
2609 ) -> Result<EncodedBuffer<CAP>, EncodeError> {
2610 let mut output = EncodedBuffer::new();
2611 let written = match self.encode_slice_clear_tail(input, &mut output.bytes) {
2612 Ok(written) => written,
2613 Err(err) => {
2614 output.clear();
2615 return Err(err);
2616 }
2617 };
2618 output.len = written;
2619 Ok(output)
2620 }
2621
2622 pub fn decode_slice(&self, input: &[u8], output: &mut [u8]) -> Result<usize, DecodeError> {
2624 match self.wrap {
2625 Some(wrap) => self.engine.decode_slice_wrapped(input, output, wrap),
2626 None => self.engine.decode_slice(input, output),
2627 }
2628 }
2629
2630 pub fn decode_slice_clear_tail(
2633 &self,
2634 input: &[u8],
2635 output: &mut [u8],
2636 ) -> Result<usize, DecodeError> {
2637 match self.wrap {
2638 Some(wrap) => self
2639 .engine
2640 .decode_slice_wrapped_clear_tail(input, output, wrap),
2641 None => self.engine.decode_slice_clear_tail(input, output),
2642 }
2643 }
2644
2645 pub fn decode_buffer<const CAP: usize>(
2651 &self,
2652 input: &[u8],
2653 ) -> Result<DecodedBuffer<CAP>, DecodeError> {
2654 let mut output = DecodedBuffer::new();
2655 let written = match self.decode_slice_clear_tail(input, &mut output.bytes) {
2656 Ok(written) => written,
2657 Err(err) => {
2658 output.clear();
2659 return Err(err);
2660 }
2661 };
2662 output.len = written;
2663 Ok(output)
2664 }
2665
2666 pub fn decode_in_place<'a>(&self, buffer: &'a mut [u8]) -> Result<&'a mut [u8], DecodeError> {
2683 match self.wrap {
2684 Some(wrap) => self.engine.decode_in_place_wrapped(buffer, wrap),
2685 None => self.engine.decode_in_place(buffer),
2686 }
2687 }
2688
2689 pub fn decode_in_place_clear_tail<'a>(
2708 &self,
2709 buffer: &'a mut [u8],
2710 ) -> Result<&'a mut [u8], DecodeError> {
2711 match self.wrap {
2712 Some(wrap) => self.engine.decode_in_place_wrapped_clear_tail(buffer, wrap),
2713 None => self.engine.decode_in_place_clear_tail(buffer),
2714 }
2715 }
2716
2717 #[cfg(feature = "alloc")]
2719 pub fn encode_vec(&self, input: &[u8]) -> Result<alloc::vec::Vec<u8>, EncodeError> {
2720 match self.wrap {
2721 Some(wrap) => self.engine.encode_wrapped_vec(input, wrap),
2722 None => self.engine.encode_vec(input),
2723 }
2724 }
2725
2726 #[cfg(feature = "alloc")]
2728 pub fn encode_secret(&self, input: &[u8]) -> Result<SecretBuffer, EncodeError> {
2729 self.encode_vec(input).map(SecretBuffer::from_vec)
2730 }
2731
2732 #[cfg(feature = "alloc")]
2734 pub fn encode_string(&self, input: &[u8]) -> Result<alloc::string::String, EncodeError> {
2735 match self.wrap {
2736 Some(wrap) => self.engine.encode_wrapped_string(input, wrap),
2737 None => self.engine.encode_string(input),
2738 }
2739 }
2740
2741 #[cfg(feature = "alloc")]
2743 pub fn decode_vec(&self, input: &[u8]) -> Result<alloc::vec::Vec<u8>, DecodeError> {
2744 match self.wrap {
2745 Some(wrap) => self.engine.decode_wrapped_vec(input, wrap),
2746 None => self.engine.decode_vec(input),
2747 }
2748 }
2749
2750 #[cfg(feature = "alloc")]
2752 pub fn decode_secret(&self, input: &[u8]) -> Result<SecretBuffer, DecodeError> {
2753 self.decode_vec(input).map(SecretBuffer::from_vec)
2754 }
2755}
2756
2757impl<A, const PAD: bool> Default for Profile<A, PAD>
2758where
2759 A: Alphabet,
2760{
2761 fn default() -> Self {
2762 Self::new(Engine::new(), None)
2763 }
2764}
2765
2766impl<A, const PAD: bool> From<Engine<A, PAD>> for Profile<A, PAD>
2767where
2768 A: Alphabet,
2769{
2770 fn from(engine: Engine<A, PAD>) -> Self {
2771 Self::new(engine, None)
2772 }
2773}
2774
2775pub const MIME: Profile<Standard, true> = Profile::new(STANDARD, Some(LineWrap::MIME));
2777
2778pub const PEM: Profile<Standard, true> = Profile::new(STANDARD, Some(LineWrap::PEM));
2780
2781pub const PEM_CRLF: Profile<Standard, true> = Profile::new(STANDARD, Some(LineWrap::PEM_CRLF));
2783
2784pub const BCRYPT: Profile<Bcrypt, false> = Profile::new(BCRYPT_NO_PAD, None);
2789
2790pub const CRYPT: Profile<Crypt, false> = Profile::new(CRYPT_NO_PAD, None);
2795
2796pub const fn encoded_len(input_len: usize, padded: bool) -> Result<usize, EncodeError> {
2811 match checked_encoded_len(input_len, padded) {
2812 Some(len) => Ok(len),
2813 None => Err(EncodeError::LengthOverflow),
2814 }
2815}
2816
2817pub const fn wrapped_encoded_len(
2831 input_len: usize,
2832 padded: bool,
2833 wrap: LineWrap,
2834) -> Result<usize, EncodeError> {
2835 if wrap.line_len == 0 {
2836 return Err(EncodeError::InvalidLineWrap { line_len: 0 });
2837 }
2838
2839 let Some(encoded) = checked_encoded_len(input_len, padded) else {
2840 return Err(EncodeError::LengthOverflow);
2841 };
2842 if encoded == 0 {
2843 return Ok(0);
2844 }
2845
2846 let breaks = (encoded - 1) / wrap.line_len;
2847 let Some(line_ending_bytes) = breaks.checked_mul(wrap.line_ending.byte_len()) else {
2848 return Err(EncodeError::LengthOverflow);
2849 };
2850 match encoded.checked_add(line_ending_bytes) {
2851 Some(len) => Ok(len),
2852 None => Err(EncodeError::LengthOverflow),
2853 }
2854}
2855
2856#[must_use]
2872pub const fn checked_wrapped_encoded_len(
2873 input_len: usize,
2874 padded: bool,
2875 wrap: LineWrap,
2876) -> Option<usize> {
2877 if wrap.line_len == 0 {
2878 return None;
2879 }
2880
2881 let Some(encoded) = checked_encoded_len(input_len, padded) else {
2882 return None;
2883 };
2884 if encoded == 0 {
2885 return Some(0);
2886 }
2887
2888 let breaks = (encoded - 1) / wrap.line_len;
2889 let Some(line_ending_bytes) = breaks.checked_mul(wrap.line_ending.byte_len()) else {
2890 return None;
2891 };
2892 encoded.checked_add(line_ending_bytes)
2893}
2894
2895#[must_use]
2906pub const fn checked_encoded_len(input_len: usize, padded: bool) -> Option<usize> {
2907 let groups = input_len / 3;
2908 if groups > usize::MAX / 4 {
2909 return None;
2910 }
2911 let full = groups * 4;
2912 let rem = input_len % 3;
2913 if rem == 0 {
2914 Some(full)
2915 } else if padded {
2916 full.checked_add(4)
2917 } else {
2918 full.checked_add(rem + 1)
2919 }
2920}
2921
2922#[must_use]
2933pub const fn decoded_capacity(encoded_len: usize) -> usize {
2934 let rem = encoded_len % 4;
2935 encoded_len / 4 * 3
2936 + if rem == 2 {
2937 1
2938 } else if rem == 3 {
2939 2
2940 } else {
2941 0
2942 }
2943}
2944
2945pub fn decoded_len(input: &[u8], padded: bool) -> Result<usize, DecodeError> {
2959 if padded {
2960 decoded_len_padded(input)
2961 } else {
2962 decoded_len_unpadded(input)
2963 }
2964}
2965
2966#[macro_export]
2998macro_rules! define_alphabet {
2999 ($(#[$meta:meta])* $vis:vis struct $name:ident = $encode:expr;) => {
3000 $(#[$meta])*
3001 #[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
3002 $vis struct $name;
3003
3004 impl $crate::Alphabet for $name {
3005 const ENCODE: [u8; 64] = *$encode;
3006
3007 #[inline]
3008 fn decode(byte: u8) -> Option<u8> {
3009 $crate::decode_alphabet_byte(byte, &Self::ENCODE)
3010 }
3011 }
3012
3013 const _: [(); 1] = [(); match $crate::validate_alphabet(
3014 &<$name as $crate::Alphabet>::ENCODE,
3015 ) {
3016 Ok(()) => 1,
3017 Err(_) => 0,
3018 }];
3019 };
3020}
3021
3022pub const fn validate_alphabet(encode: &[u8; 64]) -> Result<(), AlphabetError> {
3035 let mut index = 0;
3036 while index < encode.len() {
3037 let byte = encode[index];
3038 if !is_visible_ascii(byte) {
3039 return Err(AlphabetError::InvalidByte { index, byte });
3040 }
3041 if byte == b'=' {
3042 return Err(AlphabetError::PaddingByte { index });
3043 }
3044
3045 let mut duplicate = index + 1;
3046 while duplicate < encode.len() {
3047 if encode[duplicate] == byte {
3048 return Err(AlphabetError::DuplicateByte {
3049 first: index,
3050 second: duplicate,
3051 byte,
3052 });
3053 }
3054 duplicate += 1;
3055 }
3056
3057 index += 1;
3058 }
3059
3060 Ok(())
3061}
3062
3063#[must_use]
3089pub const fn decode_alphabet_byte(byte: u8, encode: &[u8; 64]) -> Option<u8> {
3090 let mut index = 0;
3091 let mut value = 0;
3092 while index < encode.len() {
3093 if encode[index] == byte {
3094 return Some(value);
3095 }
3096 index += 1;
3097 value += 1;
3098 }
3099 None
3100}
3101
3102pub trait Alphabet {
3104 const ENCODE: [u8; 64];
3106
3107 #[must_use]
3118 fn encode(value: u8) -> u8 {
3119 encode_alphabet_value(value, &Self::ENCODE)
3120 }
3121
3122 fn decode(byte: u8) -> Option<u8>;
3124}
3125
3126const fn is_visible_ascii(byte: u8) -> bool {
3127 byte >= 0x21 && byte <= 0x7e
3128}
3129
3130#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
3132pub struct Standard;
3133
3134impl Alphabet for Standard {
3135 const ENCODE: [u8; 64] = *b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
3136
3137 #[inline]
3138 fn encode(value: u8) -> u8 {
3139 encode_ascii_base64(value, Self::ENCODE[62], Self::ENCODE[63])
3140 }
3141
3142 #[inline]
3143 fn decode(byte: u8) -> Option<u8> {
3144 decode_ascii_base64(byte, Self::ENCODE[62], Self::ENCODE[63])
3145 }
3146}
3147
3148#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
3150pub struct UrlSafe;
3151
3152impl Alphabet for UrlSafe {
3153 const ENCODE: [u8; 64] = *b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
3154
3155 #[inline]
3156 fn encode(value: u8) -> u8 {
3157 encode_ascii_base64(value, Self::ENCODE[62], Self::ENCODE[63])
3158 }
3159
3160 #[inline]
3161 fn decode(byte: u8) -> Option<u8> {
3162 decode_ascii_base64(byte, Self::ENCODE[62], Self::ENCODE[63])
3163 }
3164}
3165
3166#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
3172pub struct Bcrypt;
3173
3174impl Alphabet for Bcrypt {
3175 const ENCODE: [u8; 64] = *b"./ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
3176
3177 #[inline]
3178 fn decode(byte: u8) -> Option<u8> {
3179 decode_alphabet_byte(byte, &Self::ENCODE)
3180 }
3181}
3182
3183#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
3188pub struct Crypt;
3189
3190impl Alphabet for Crypt {
3191 const ENCODE: [u8; 64] = *b"./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
3192
3193 #[inline]
3194 fn decode(byte: u8) -> Option<u8> {
3195 decode_alphabet_byte(byte, &Self::ENCODE)
3196 }
3197}
3198
3199#[inline]
3200const fn encode_base64_value<A: Alphabet>(value: u8) -> u8 {
3201 encode_alphabet_value(value, &A::ENCODE)
3202}
3203
3204#[inline]
3205fn encode_base64_value_runtime<A: Alphabet>(value: u8) -> u8 {
3206 A::encode(value)
3207}
3208
3209#[inline]
3210const fn encode_alphabet_value(value: u8, encode: &[u8; 64]) -> u8 {
3211 let mut output = 0;
3212 let mut index = 0;
3213 let mut candidate = 0;
3214 while index < encode.len() {
3215 output |= encode[index] & ct_mask_eq_u8(value, candidate);
3216 index += 1;
3217 candidate += 1;
3218 }
3219 output
3220}
3221
3222#[inline]
3223const fn encode_ascii_base64(value: u8, value_62_byte: u8, value_63_byte: u8) -> u8 {
3224 let upper = ct_mask_lt_u8(value, 26);
3225 let lower = ct_mask_lt_u8(value.wrapping_sub(26), 26);
3226 let digit = ct_mask_lt_u8(value.wrapping_sub(52), 10);
3227 let value_62 = ct_mask_eq_u8(value, 0x3e);
3228 let value_63 = ct_mask_eq_u8(value, 0x3f);
3229
3230 (value.wrapping_add(b'A') & upper)
3231 | (value.wrapping_sub(26).wrapping_add(b'a') & lower)
3232 | (value.wrapping_sub(52).wrapping_add(b'0') & digit)
3233 | (value_62_byte & value_62)
3234 | (value_63_byte & value_63)
3235}
3236
3237#[inline]
3238fn decode_ascii_base64(byte: u8, value_62_byte: u8, value_63_byte: u8) -> Option<u8> {
3239 let upper = ct_mask_lt_u8(byte.wrapping_sub(b'A'), 26);
3240 let lower = ct_mask_lt_u8(byte.wrapping_sub(b'a'), 26);
3241 let digit = ct_mask_lt_u8(byte.wrapping_sub(b'0'), 10);
3242 let value_62 = ct_mask_eq_u8(byte, value_62_byte);
3243 let value_63 = ct_mask_eq_u8(byte, value_63_byte);
3244 let valid = upper | lower | digit | value_62 | value_63;
3245
3246 let decoded = (byte.wrapping_sub(b'A') & upper)
3247 | (byte.wrapping_sub(b'a').wrapping_add(26) & lower)
3248 | (byte.wrapping_sub(b'0').wrapping_add(52) & digit)
3249 | (0x3e & value_62)
3250 | (0x3f & value_63);
3251
3252 if valid == 0 { None } else { Some(decoded) }
3253}
3254
3255#[inline]
3256const fn ct_mask_bit(bit: u8) -> u8 {
3257 0u8.wrapping_sub(bit & 1)
3258}
3259
3260#[inline]
3261const fn ct_mask_nonzero_u8(value: u8) -> u8 {
3262 let wide = value as u16;
3263 let negative = 0u16.wrapping_sub(wide);
3264 let nonzero = ((wide | negative) >> 8) as u8;
3265 ct_mask_bit(nonzero)
3266}
3267
3268#[inline]
3269const fn ct_mask_eq_u8(left: u8, right: u8) -> u8 {
3270 !ct_mask_nonzero_u8(left ^ right)
3271}
3272
3273#[inline]
3274const fn ct_mask_lt_u8(left: u8, right: u8) -> u8 {
3275 let diff = (left as u16).wrapping_sub(right as u16);
3276 ct_mask_bit((diff >> 8) as u8)
3277}
3278
3279fn constant_time_eq_public_len(left: &[u8], right: &[u8]) -> bool {
3280 if left.len() != right.len() {
3281 return false;
3282 }
3283
3284 let diff = left
3285 .iter()
3286 .zip(right)
3287 .fold(0u8, |diff, (left, right)| diff | (*left ^ *right));
3288 diff == 0
3289}
3290
3291mod backend {
3292 use super::{
3293 Alphabet, DecodeError, EncodeError, checked_encoded_len, decode_padded, decode_unpadded,
3294 encode_base64_value_runtime,
3295 };
3296
3297 pub(super) fn encode_slice<A, const PAD: bool>(
3298 input: &[u8],
3299 output: &mut [u8],
3300 ) -> Result<usize, EncodeError>
3301 where
3302 A: Alphabet,
3303 {
3304 #[cfg(feature = "simd")]
3305 match super::simd::active_backend() {
3306 super::simd::ActiveBackend::Scalar => {}
3307 }
3308
3309 scalar_encode_slice::<A, PAD>(input, output)
3310 }
3311
3312 pub(super) fn decode_slice<A, const PAD: bool>(
3313 input: &[u8],
3314 output: &mut [u8],
3315 ) -> Result<usize, DecodeError>
3316 where
3317 A: Alphabet,
3318 {
3319 #[cfg(feature = "simd")]
3320 match super::simd::active_backend() {
3321 super::simd::ActiveBackend::Scalar => {}
3322 }
3323
3324 scalar_decode_slice::<A, PAD>(input, output)
3325 }
3326
3327 #[cfg(test)]
3328 pub(super) fn scalar_reference_encode_slice<A, const PAD: bool>(
3329 input: &[u8],
3330 output: &mut [u8],
3331 ) -> Result<usize, EncodeError>
3332 where
3333 A: Alphabet,
3334 {
3335 scalar_encode_slice::<A, PAD>(input, output)
3336 }
3337
3338 #[cfg(test)]
3339 pub(super) fn scalar_reference_decode_slice<A, const PAD: bool>(
3340 input: &[u8],
3341 output: &mut [u8],
3342 ) -> Result<usize, DecodeError>
3343 where
3344 A: Alphabet,
3345 {
3346 scalar_decode_slice::<A, PAD>(input, output)
3347 }
3348
3349 fn scalar_encode_slice<A, const PAD: bool>(
3350 input: &[u8],
3351 output: &mut [u8],
3352 ) -> Result<usize, EncodeError>
3353 where
3354 A: Alphabet,
3355 {
3356 let required = checked_encoded_len(input.len(), PAD).ok_or(EncodeError::LengthOverflow)?;
3357 if output.len() < required {
3358 return Err(EncodeError::OutputTooSmall {
3359 required,
3360 available: output.len(),
3361 });
3362 }
3363
3364 let mut read = 0;
3365 let mut write = 0;
3366 while read + 3 <= input.len() {
3367 let b0 = input[read];
3368 let b1 = input[read + 1];
3369 let b2 = input[read + 2];
3370
3371 output[write] = encode_base64_value_runtime::<A>(b0 >> 2);
3372 output[write + 1] =
3373 encode_base64_value_runtime::<A>(((b0 & 0b0000_0011) << 4) | (b1 >> 4));
3374 output[write + 2] =
3375 encode_base64_value_runtime::<A>(((b1 & 0b0000_1111) << 2) | (b2 >> 6));
3376 output[write + 3] = encode_base64_value_runtime::<A>(b2 & 0b0011_1111);
3377
3378 read += 3;
3379 write += 4;
3380 }
3381
3382 match input.len() - read {
3383 0 => {}
3384 1 => {
3385 let b0 = input[read];
3386 output[write] = encode_base64_value_runtime::<A>(b0 >> 2);
3387 output[write + 1] = encode_base64_value_runtime::<A>((b0 & 0b0000_0011) << 4);
3388 write += 2;
3389 if PAD {
3390 output[write] = b'=';
3391 output[write + 1] = b'=';
3392 write += 2;
3393 }
3394 }
3395 2 => {
3396 let b0 = input[read];
3397 let b1 = input[read + 1];
3398 output[write] = encode_base64_value_runtime::<A>(b0 >> 2);
3399 output[write + 1] =
3400 encode_base64_value_runtime::<A>(((b0 & 0b0000_0011) << 4) | (b1 >> 4));
3401 output[write + 2] = encode_base64_value_runtime::<A>((b1 & 0b0000_1111) << 2);
3402 write += 3;
3403 if PAD {
3404 output[write] = b'=';
3405 write += 1;
3406 }
3407 }
3408 _ => unreachable!(),
3409 }
3410
3411 Ok(write)
3412 }
3413
3414 fn scalar_decode_slice<A, const PAD: bool>(
3415 input: &[u8],
3416 output: &mut [u8],
3417 ) -> Result<usize, DecodeError>
3418 where
3419 A: Alphabet,
3420 {
3421 if input.is_empty() {
3422 return Ok(0);
3423 }
3424
3425 if PAD {
3426 decode_padded::<A>(input, output)
3427 } else {
3428 decode_unpadded::<A>(input, output)
3429 }
3430 }
3431}
3432
3433pub struct Engine<A, const PAD: bool> {
3435 alphabet: core::marker::PhantomData<A>,
3436}
3437
3438impl<A, const PAD: bool> Clone for Engine<A, PAD> {
3439 fn clone(&self) -> Self {
3440 *self
3441 }
3442}
3443
3444impl<A, const PAD: bool> Copy for Engine<A, PAD> {}
3445
3446impl<A, const PAD: bool> core::fmt::Debug for Engine<A, PAD> {
3447 fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
3448 formatter
3449 .debug_struct("Engine")
3450 .field("padded", &PAD)
3451 .finish()
3452 }
3453}
3454
3455impl<A, const PAD: bool> Default for Engine<A, PAD> {
3456 fn default() -> Self {
3457 Self {
3458 alphabet: core::marker::PhantomData,
3459 }
3460 }
3461}
3462
3463impl<A, const PAD: bool> Eq for Engine<A, PAD> {}
3464
3465impl<A, const PAD: bool> PartialEq for Engine<A, PAD> {
3466 fn eq(&self, _other: &Self) -> bool {
3467 true
3468 }
3469}
3470
3471impl<A, const PAD: bool> Engine<A, PAD>
3472where
3473 A: Alphabet,
3474{
3475 #[must_use]
3477 pub const fn new() -> Self {
3478 Self {
3479 alphabet: core::marker::PhantomData,
3480 }
3481 }
3482
3483 #[must_use]
3485 pub const fn is_padded(&self) -> bool {
3486 PAD
3487 }
3488
3489 pub const fn encoded_len(&self, input_len: usize) -> Result<usize, EncodeError> {
3491 encoded_len(input_len, PAD)
3492 }
3493
3494 #[must_use]
3496 pub const fn checked_encoded_len(&self, input_len: usize) -> Option<usize> {
3497 checked_encoded_len(input_len, PAD)
3498 }
3499
3500 pub const fn wrapped_encoded_len(
3505 &self,
3506 input_len: usize,
3507 wrap: LineWrap,
3508 ) -> Result<usize, EncodeError> {
3509 wrapped_encoded_len(input_len, PAD, wrap)
3510 }
3511
3512 #[must_use]
3515 pub const fn checked_wrapped_encoded_len(
3516 &self,
3517 input_len: usize,
3518 wrap: LineWrap,
3519 ) -> Option<usize> {
3520 checked_wrapped_encoded_len(input_len, PAD, wrap)
3521 }
3522
3523 pub fn decoded_len(&self, input: &[u8]) -> Result<usize, DecodeError> {
3528 decoded_len(input, PAD)
3529 }
3530
3531 pub fn decoded_len_legacy(&self, input: &[u8]) -> Result<usize, DecodeError> {
3537 validate_legacy_decode::<A, PAD>(input)
3538 }
3539
3540 pub fn decoded_len_wrapped(&self, input: &[u8], wrap: LineWrap) -> Result<usize, DecodeError> {
3547 validate_wrapped_decode::<A, PAD>(input, wrap)
3548 }
3549
3550 pub fn validate_result(&self, input: &[u8]) -> Result<(), DecodeError> {
3565 validate_decode::<A, PAD>(input).map(|_| ())
3566 }
3567
3568 #[must_use]
3581 pub fn validate(&self, input: &[u8]) -> bool {
3582 self.validate_result(input).is_ok()
3583 }
3584
3585 pub fn validate_legacy_result(&self, input: &[u8]) -> Result<(), DecodeError> {
3600 validate_legacy_decode::<A, PAD>(input).map(|_| ())
3601 }
3602
3603 #[must_use]
3617 pub fn validate_legacy(&self, input: &[u8]) -> bool {
3618 self.validate_legacy_result(input).is_ok()
3619 }
3620
3621 pub fn validate_wrapped_result(&self, input: &[u8], wrap: LineWrap) -> Result<(), DecodeError> {
3637 validate_wrapped_decode::<A, PAD>(input, wrap).map(|_| ())
3638 }
3639
3640 #[must_use]
3654 pub fn validate_wrapped(&self, input: &[u8], wrap: LineWrap) -> bool {
3655 self.validate_wrapped_result(input, wrap).is_ok()
3656 }
3657
3658 #[must_use]
3690 pub const fn encode_array<const INPUT_LEN: usize, const OUTPUT_LEN: usize>(
3691 &self,
3692 input: &[u8; INPUT_LEN],
3693 ) -> [u8; OUTPUT_LEN] {
3694 let Some(required) = checked_encoded_len(INPUT_LEN, PAD) else {
3695 panic!("encoded base64 length overflows usize");
3696 };
3697 assert!(
3698 required == OUTPUT_LEN,
3699 "base64 output array has incorrect length"
3700 );
3701
3702 let mut output = [0u8; OUTPUT_LEN];
3703 let mut read = 0;
3704 let mut write = 0;
3705 while INPUT_LEN - read >= 3 {
3706 let b0 = input[read];
3707 let b1 = input[read + 1];
3708 let b2 = input[read + 2];
3709
3710 output[write] = encode_base64_value::<A>(b0 >> 2);
3711 output[write + 1] = encode_base64_value::<A>(((b0 & 0b0000_0011) << 4) | (b1 >> 4));
3712 output[write + 2] = encode_base64_value::<A>(((b1 & 0b0000_1111) << 2) | (b2 >> 6));
3713 output[write + 3] = encode_base64_value::<A>(b2 & 0b0011_1111);
3714
3715 read += 3;
3716 write += 4;
3717 }
3718
3719 match INPUT_LEN - read {
3720 0 => {}
3721 1 => {
3722 let b0 = input[read];
3723 output[write] = encode_base64_value::<A>(b0 >> 2);
3724 output[write + 1] = encode_base64_value::<A>((b0 & 0b0000_0011) << 4);
3725 write += 2;
3726 if PAD {
3727 output[write] = b'=';
3728 output[write + 1] = b'=';
3729 }
3730 }
3731 2 => {
3732 let b0 = input[read];
3733 let b1 = input[read + 1];
3734 output[write] = encode_base64_value::<A>(b0 >> 2);
3735 output[write + 1] = encode_base64_value::<A>(((b0 & 0b0000_0011) << 4) | (b1 >> 4));
3736 output[write + 2] = encode_base64_value::<A>((b1 & 0b0000_1111) << 2);
3737 if PAD {
3738 output[write + 3] = b'=';
3739 }
3740 }
3741 _ => unreachable!(),
3742 }
3743
3744 output
3745 }
3746
3747 pub fn encode_slice(&self, input: &[u8], output: &mut [u8]) -> Result<usize, EncodeError> {
3749 backend::encode_slice::<A, PAD>(input, output)
3750 }
3751
3752 pub fn encode_slice_wrapped(
3771 &self,
3772 input: &[u8],
3773 output: &mut [u8],
3774 wrap: LineWrap,
3775 ) -> Result<usize, EncodeError> {
3776 let required = self.wrapped_encoded_len(input.len(), wrap)?;
3777 if output.len() < required {
3778 return Err(EncodeError::OutputTooSmall {
3779 required,
3780 available: output.len(),
3781 });
3782 }
3783
3784 let encoded_len =
3785 checked_encoded_len(input.len(), PAD).ok_or(EncodeError::LengthOverflow)?;
3786 if encoded_len == 0 {
3787 return Ok(0);
3788 }
3789
3790 if output.len() < required.saturating_add(encoded_len) {
3791 let mut scratch = [0u8; 1024];
3792 let mut input_offset = 0;
3793 let mut output_offset = 0;
3794 let mut column = 0;
3795
3796 while input_offset < input.len() {
3797 let remaining = input.len() - input_offset;
3798 let mut take = remaining.min(768);
3799 if remaining > take {
3800 take -= take % 3;
3801 }
3802 if take == 0 {
3803 take = remaining;
3804 }
3805
3806 let encoded =
3807 self.encode_slice(&input[input_offset..input_offset + take], &mut scratch)?;
3808 write_wrapped_bytes(
3809 &scratch[..encoded],
3810 output,
3811 &mut output_offset,
3812 &mut column,
3813 wrap,
3814 );
3815 wipe_bytes(&mut scratch[..encoded]);
3816 input_offset += take;
3817 }
3818
3819 Ok(output_offset)
3820 } else {
3821 let encoded =
3822 self.encode_slice(input, &mut output[required..required + encoded_len])?;
3823 let mut output_offset = 0;
3824 let mut column = 0;
3825 let mut read = required;
3826 while read < required + encoded {
3827 let byte = output[read];
3828 write_wrapped_byte(byte, output, &mut output_offset, &mut column, wrap);
3829 read += 1;
3830 }
3831 wipe_bytes(&mut output[required..required + encoded]);
3832 Ok(output_offset)
3833 }
3834 }
3835
3836 pub fn encode_slice_wrapped_clear_tail(
3842 &self,
3843 input: &[u8],
3844 output: &mut [u8],
3845 wrap: LineWrap,
3846 ) -> Result<usize, EncodeError> {
3847 let written = match self.encode_slice_wrapped(input, output, wrap) {
3848 Ok(written) => written,
3849 Err(err) => {
3850 wipe_bytes(output);
3851 return Err(err);
3852 }
3853 };
3854 wipe_tail(output, written);
3855 Ok(written)
3856 }
3857
3858 pub fn encode_wrapped_buffer<const CAP: usize>(
3864 &self,
3865 input: &[u8],
3866 wrap: LineWrap,
3867 ) -> Result<EncodedBuffer<CAP>, EncodeError> {
3868 let mut output = EncodedBuffer::new();
3869 let written = match self.encode_slice_wrapped_clear_tail(input, &mut output.bytes, wrap) {
3870 Ok(written) => written,
3871 Err(err) => {
3872 output.clear();
3873 return Err(err);
3874 }
3875 };
3876 output.len = written;
3877 Ok(output)
3878 }
3879
3880 #[cfg(feature = "alloc")]
3882 pub fn encode_wrapped_vec(
3883 &self,
3884 input: &[u8],
3885 wrap: LineWrap,
3886 ) -> Result<alloc::vec::Vec<u8>, EncodeError> {
3887 let required = self.wrapped_encoded_len(input.len(), wrap)?;
3888 let mut output = alloc::vec![0; required];
3889 let written = self.encode_slice_wrapped(input, &mut output, wrap)?;
3890 output.truncate(written);
3891 Ok(output)
3892 }
3893
3894 #[cfg(feature = "alloc")]
3896 pub fn encode_wrapped_string(
3897 &self,
3898 input: &[u8],
3899 wrap: LineWrap,
3900 ) -> Result<alloc::string::String, EncodeError> {
3901 let output = self.encode_wrapped_vec(input, wrap)?;
3902 match alloc::string::String::from_utf8(output) {
3903 Ok(output) => Ok(output),
3904 Err(_) => unreachable!("base64 encoder produced non-UTF-8 output"),
3905 }
3906 }
3907
3908 #[cfg(feature = "alloc")]
3913 pub fn encode_wrapped_secret(
3914 &self,
3915 input: &[u8],
3916 wrap: LineWrap,
3917 ) -> Result<SecretBuffer, EncodeError> {
3918 self.encode_wrapped_vec(input, wrap)
3919 .map(SecretBuffer::from_vec)
3920 }
3921
3922 pub fn encode_slice_clear_tail(
3942 &self,
3943 input: &[u8],
3944 output: &mut [u8],
3945 ) -> Result<usize, EncodeError> {
3946 let written = match self.encode_slice(input, output) {
3947 Ok(written) => written,
3948 Err(err) => {
3949 wipe_bytes(output);
3950 return Err(err);
3951 }
3952 };
3953 wipe_tail(output, written);
3954 Ok(written)
3955 }
3956
3957 pub fn encode_buffer<const CAP: usize>(
3972 &self,
3973 input: &[u8],
3974 ) -> Result<EncodedBuffer<CAP>, EncodeError> {
3975 let mut output = EncodedBuffer::new();
3976 let written = match self.encode_slice_clear_tail(input, &mut output.bytes) {
3977 Ok(written) => written,
3978 Err(err) => {
3979 output.clear();
3980 return Err(err);
3981 }
3982 };
3983 output.len = written;
3984 Ok(output)
3985 }
3986
3987 #[cfg(feature = "alloc")]
3989 pub fn encode_vec(&self, input: &[u8]) -> Result<alloc::vec::Vec<u8>, EncodeError> {
3990 let required = checked_encoded_len(input.len(), PAD).ok_or(EncodeError::LengthOverflow)?;
3991 let mut output = alloc::vec![0; required];
3992 let written = self.encode_slice(input, &mut output)?;
3993 output.truncate(written);
3994 Ok(output)
3995 }
3996
3997 #[cfg(feature = "alloc")]
4002 pub fn encode_secret(&self, input: &[u8]) -> Result<SecretBuffer, EncodeError> {
4003 self.encode_vec(input).map(SecretBuffer::from_vec)
4004 }
4005
4006 #[cfg(feature = "alloc")]
4021 pub fn encode_string(&self, input: &[u8]) -> Result<alloc::string::String, EncodeError> {
4022 let output = self.encode_vec(input)?;
4023 match alloc::string::String::from_utf8(output) {
4024 Ok(output) => Ok(output),
4025 Err(_) => unreachable!("base64 encoder produced non-UTF-8 output"),
4026 }
4027 }
4028
4029 pub fn encode_in_place<'a>(
4046 &self,
4047 buffer: &'a mut [u8],
4048 input_len: usize,
4049 ) -> Result<&'a mut [u8], EncodeError> {
4050 if input_len > buffer.len() {
4051 return Err(EncodeError::InputTooLarge {
4052 input_len,
4053 buffer_len: buffer.len(),
4054 });
4055 }
4056
4057 let required = checked_encoded_len(input_len, PAD).ok_or(EncodeError::LengthOverflow)?;
4058 if buffer.len() < required {
4059 return Err(EncodeError::OutputTooSmall {
4060 required,
4061 available: buffer.len(),
4062 });
4063 }
4064
4065 let mut read = input_len;
4066 let mut write = required;
4067
4068 match input_len % 3 {
4069 0 => {}
4070 1 => {
4071 read -= 1;
4072 let b0 = buffer[read];
4073 if PAD {
4074 write -= 4;
4075 buffer[write] = encode_base64_value_runtime::<A>(b0 >> 2);
4076 buffer[write + 1] = encode_base64_value_runtime::<A>((b0 & 0b0000_0011) << 4);
4077 buffer[write + 2] = b'=';
4078 buffer[write + 3] = b'=';
4079 } else {
4080 write -= 2;
4081 buffer[write] = encode_base64_value_runtime::<A>(b0 >> 2);
4082 buffer[write + 1] = encode_base64_value_runtime::<A>((b0 & 0b0000_0011) << 4);
4083 }
4084 }
4085 2 => {
4086 read -= 2;
4087 let b0 = buffer[read];
4088 let b1 = buffer[read + 1];
4089 if PAD {
4090 write -= 4;
4091 buffer[write] = encode_base64_value_runtime::<A>(b0 >> 2);
4092 buffer[write + 1] =
4093 encode_base64_value_runtime::<A>(((b0 & 0b0000_0011) << 4) | (b1 >> 4));
4094 buffer[write + 2] = encode_base64_value_runtime::<A>((b1 & 0b0000_1111) << 2);
4095 buffer[write + 3] = b'=';
4096 } else {
4097 write -= 3;
4098 buffer[write] = encode_base64_value_runtime::<A>(b0 >> 2);
4099 buffer[write + 1] =
4100 encode_base64_value_runtime::<A>(((b0 & 0b0000_0011) << 4) | (b1 >> 4));
4101 buffer[write + 2] = encode_base64_value_runtime::<A>((b1 & 0b0000_1111) << 2);
4102 }
4103 }
4104 _ => unreachable!(),
4105 }
4106
4107 while read > 0 {
4108 read -= 3;
4109 write -= 4;
4110 let b0 = buffer[read];
4111 let b1 = buffer[read + 1];
4112 let b2 = buffer[read + 2];
4113
4114 buffer[write] = encode_base64_value_runtime::<A>(b0 >> 2);
4115 buffer[write + 1] =
4116 encode_base64_value_runtime::<A>(((b0 & 0b0000_0011) << 4) | (b1 >> 4));
4117 buffer[write + 2] =
4118 encode_base64_value_runtime::<A>(((b1 & 0b0000_1111) << 2) | (b2 >> 6));
4119 buffer[write + 3] = encode_base64_value_runtime::<A>(b2 & 0b0011_1111);
4120 }
4121
4122 debug_assert_eq!(write, 0);
4123 Ok(&mut buffer[..required])
4124 }
4125
4126 pub fn encode_in_place_clear_tail<'a>(
4144 &self,
4145 buffer: &'a mut [u8],
4146 input_len: usize,
4147 ) -> Result<&'a mut [u8], EncodeError> {
4148 let len = match self.encode_in_place(buffer, input_len) {
4149 Ok(encoded) => encoded.len(),
4150 Err(err) => {
4151 wipe_bytes(buffer);
4152 return Err(err);
4153 }
4154 };
4155 wipe_tail(buffer, len);
4156 Ok(&mut buffer[..len])
4157 }
4158
4159 pub fn decode_slice(&self, input: &[u8], output: &mut [u8]) -> Result<usize, DecodeError> {
4164 backend::decode_slice::<A, PAD>(input, output)
4165 }
4166
4167 pub fn decode_slice_clear_tail(
4187 &self,
4188 input: &[u8],
4189 output: &mut [u8],
4190 ) -> Result<usize, DecodeError> {
4191 let written = match self.decode_slice(input, output) {
4192 Ok(written) => written,
4193 Err(err) => {
4194 wipe_bytes(output);
4195 return Err(err);
4196 }
4197 };
4198 wipe_tail(output, written);
4199 Ok(written)
4200 }
4201
4202 pub fn decode_buffer<const CAP: usize>(
4217 &self,
4218 input: &[u8],
4219 ) -> Result<DecodedBuffer<CAP>, DecodeError> {
4220 let mut output = DecodedBuffer::new();
4221 let written = match self.decode_slice_clear_tail(input, &mut output.bytes) {
4222 Ok(written) => written,
4223 Err(err) => {
4224 output.clear();
4225 return Err(err);
4226 }
4227 };
4228 output.len = written;
4229 Ok(output)
4230 }
4231
4232 pub fn decode_slice_legacy(
4238 &self,
4239 input: &[u8],
4240 output: &mut [u8],
4241 ) -> Result<usize, DecodeError> {
4242 let required = validate_legacy_decode::<A, PAD>(input)?;
4243 if output.len() < required {
4244 return Err(DecodeError::OutputTooSmall {
4245 required,
4246 available: output.len(),
4247 });
4248 }
4249 decode_legacy_to_slice::<A, PAD>(input, output)
4250 }
4251
4252 pub fn decode_slice_legacy_clear_tail(
4272 &self,
4273 input: &[u8],
4274 output: &mut [u8],
4275 ) -> Result<usize, DecodeError> {
4276 let written = match self.decode_slice_legacy(input, output) {
4277 Ok(written) => written,
4278 Err(err) => {
4279 wipe_bytes(output);
4280 return Err(err);
4281 }
4282 };
4283 wipe_tail(output, written);
4284 Ok(written)
4285 }
4286
4287 pub fn decode_buffer_legacy<const CAP: usize>(
4295 &self,
4296 input: &[u8],
4297 ) -> Result<DecodedBuffer<CAP>, DecodeError> {
4298 let mut output = DecodedBuffer::new();
4299 let written = match self.decode_slice_legacy_clear_tail(input, &mut output.bytes) {
4300 Ok(written) => written,
4301 Err(err) => {
4302 output.clear();
4303 return Err(err);
4304 }
4305 };
4306 output.len = written;
4307 Ok(output)
4308 }
4309
4310 pub fn decode_slice_wrapped(
4317 &self,
4318 input: &[u8],
4319 output: &mut [u8],
4320 wrap: LineWrap,
4321 ) -> Result<usize, DecodeError> {
4322 let required = validate_wrapped_decode::<A, PAD>(input, wrap)?;
4323 if output.len() < required {
4324 return Err(DecodeError::OutputTooSmall {
4325 required,
4326 available: output.len(),
4327 });
4328 }
4329 decode_wrapped_to_slice::<A, PAD>(input, output, wrap)
4330 }
4331
4332 pub fn decode_slice_wrapped_clear_tail(
4338 &self,
4339 input: &[u8],
4340 output: &mut [u8],
4341 wrap: LineWrap,
4342 ) -> Result<usize, DecodeError> {
4343 let written = match self.decode_slice_wrapped(input, output, wrap) {
4344 Ok(written) => written,
4345 Err(err) => {
4346 wipe_bytes(output);
4347 return Err(err);
4348 }
4349 };
4350 wipe_tail(output, written);
4351 Ok(written)
4352 }
4353
4354 pub fn decode_wrapped_buffer<const CAP: usize>(
4363 &self,
4364 input: &[u8],
4365 wrap: LineWrap,
4366 ) -> Result<DecodedBuffer<CAP>, DecodeError> {
4367 let mut output = DecodedBuffer::new();
4368 let written = match self.decode_slice_wrapped_clear_tail(input, &mut output.bytes, wrap) {
4369 Ok(written) => written,
4370 Err(err) => {
4371 output.clear();
4372 return Err(err);
4373 }
4374 };
4375 output.len = written;
4376 Ok(output)
4377 }
4378
4379 #[cfg(feature = "alloc")]
4383 pub fn decode_vec(&self, input: &[u8]) -> Result<alloc::vec::Vec<u8>, DecodeError> {
4384 let required = validate_decode::<A, PAD>(input)?;
4385 let mut output = alloc::vec![0; required];
4386 let written = match self.decode_slice(input, &mut output) {
4387 Ok(written) => written,
4388 Err(err) => {
4389 wipe_bytes(&mut output);
4390 return Err(err);
4391 }
4392 };
4393 output.truncate(written);
4394 Ok(output)
4395 }
4396
4397 #[cfg(feature = "alloc")]
4402 pub fn decode_secret(&self, input: &[u8]) -> Result<SecretBuffer, DecodeError> {
4403 self.decode_vec(input).map(SecretBuffer::from_vec)
4404 }
4405
4406 #[cfg(feature = "alloc")]
4409 pub fn decode_vec_legacy(&self, input: &[u8]) -> Result<alloc::vec::Vec<u8>, DecodeError> {
4410 let required = validate_legacy_decode::<A, PAD>(input)?;
4411 let mut output = alloc::vec![0; required];
4412 let written = match self.decode_slice_legacy(input, &mut output) {
4413 Ok(written) => written,
4414 Err(err) => {
4415 wipe_bytes(&mut output);
4416 return Err(err);
4417 }
4418 };
4419 output.truncate(written);
4420 Ok(output)
4421 }
4422
4423 #[cfg(feature = "alloc")]
4430 pub fn decode_secret_legacy(&self, input: &[u8]) -> Result<SecretBuffer, DecodeError> {
4431 self.decode_vec_legacy(input).map(SecretBuffer::from_vec)
4432 }
4433
4434 #[cfg(feature = "alloc")]
4436 pub fn decode_wrapped_vec(
4437 &self,
4438 input: &[u8],
4439 wrap: LineWrap,
4440 ) -> Result<alloc::vec::Vec<u8>, DecodeError> {
4441 let required = validate_wrapped_decode::<A, PAD>(input, wrap)?;
4442 let mut output = alloc::vec![0; required];
4443 let written = match self.decode_slice_wrapped(input, &mut output, wrap) {
4444 Ok(written) => written,
4445 Err(err) => {
4446 wipe_bytes(&mut output);
4447 return Err(err);
4448 }
4449 };
4450 output.truncate(written);
4451 Ok(output)
4452 }
4453
4454 #[cfg(feature = "alloc")]
4461 pub fn decode_wrapped_secret(
4462 &self,
4463 input: &[u8],
4464 wrap: LineWrap,
4465 ) -> Result<SecretBuffer, DecodeError> {
4466 self.decode_wrapped_vec(input, wrap)
4467 .map(SecretBuffer::from_vec)
4468 }
4469
4470 pub fn decode_in_place_wrapped<'a>(
4490 &self,
4491 buffer: &'a mut [u8],
4492 wrap: LineWrap,
4493 ) -> Result<&'a mut [u8], DecodeError> {
4494 let _required = validate_wrapped_decode::<A, PAD>(buffer, wrap)?;
4495 let compacted = compact_wrapped_input(buffer, wrap)?;
4496 let len = Self::decode_slice_to_start(&mut buffer[..compacted])?;
4497 Ok(&mut buffer[..len])
4498 }
4499
4500 pub fn decode_in_place_wrapped_clear_tail<'a>(
4521 &self,
4522 buffer: &'a mut [u8],
4523 wrap: LineWrap,
4524 ) -> Result<&'a mut [u8], DecodeError> {
4525 if let Err(err) = validate_wrapped_decode::<A, PAD>(buffer, wrap) {
4526 wipe_bytes(buffer);
4527 return Err(err);
4528 }
4529
4530 let compacted = match compact_wrapped_input(buffer, wrap) {
4531 Ok(compacted) => compacted,
4532 Err(err) => {
4533 wipe_bytes(buffer);
4534 return Err(err);
4535 }
4536 };
4537
4538 let len = match Self::decode_slice_to_start(&mut buffer[..compacted]) {
4539 Ok(len) => len,
4540 Err(err) => {
4541 wipe_bytes(buffer);
4542 return Err(err);
4543 }
4544 };
4545 wipe_tail(buffer, len);
4546 Ok(&mut buffer[..len])
4547 }
4548
4549 pub fn decode_in_place<'a>(&self, buffer: &'a mut [u8]) -> Result<&'a mut [u8], DecodeError> {
4561 let len = Self::decode_slice_to_start(buffer)?;
4562 Ok(&mut buffer[..len])
4563 }
4564
4565 pub fn decode_in_place_clear_tail<'a>(
4582 &self,
4583 buffer: &'a mut [u8],
4584 ) -> Result<&'a mut [u8], DecodeError> {
4585 let len = match Self::decode_slice_to_start(buffer) {
4586 Ok(len) => len,
4587 Err(err) => {
4588 wipe_bytes(buffer);
4589 return Err(err);
4590 }
4591 };
4592 wipe_tail(buffer, len);
4593 Ok(&mut buffer[..len])
4594 }
4595
4596 pub fn decode_in_place_legacy<'a>(
4601 &self,
4602 buffer: &'a mut [u8],
4603 ) -> Result<&'a mut [u8], DecodeError> {
4604 let _required = validate_legacy_decode::<A, PAD>(buffer)?;
4605 let mut write = 0;
4606 let mut read = 0;
4607 while read < buffer.len() {
4608 let byte = buffer[read];
4609 if !is_legacy_whitespace(byte) {
4610 buffer[write] = byte;
4611 write += 1;
4612 }
4613 read += 1;
4614 }
4615 let len = Self::decode_slice_to_start(&mut buffer[..write])?;
4616 Ok(&mut buffer[..len])
4617 }
4618
4619 pub fn decode_in_place_legacy_clear_tail<'a>(
4625 &self,
4626 buffer: &'a mut [u8],
4627 ) -> Result<&'a mut [u8], DecodeError> {
4628 if let Err(err) = validate_legacy_decode::<A, PAD>(buffer) {
4629 wipe_bytes(buffer);
4630 return Err(err);
4631 }
4632
4633 let mut write = 0;
4634 let mut read = 0;
4635 while read < buffer.len() {
4636 let byte = buffer[read];
4637 if !is_legacy_whitespace(byte) {
4638 buffer[write] = byte;
4639 write += 1;
4640 }
4641 read += 1;
4642 }
4643
4644 let len = match Self::decode_slice_to_start(&mut buffer[..write]) {
4645 Ok(len) => len,
4646 Err(err) => {
4647 wipe_bytes(buffer);
4648 return Err(err);
4649 }
4650 };
4651 wipe_tail(buffer, len);
4652 Ok(&mut buffer[..len])
4653 }
4654
4655 fn decode_slice_to_start(buffer: &mut [u8]) -> Result<usize, DecodeError> {
4656 let input_len = buffer.len();
4657 let mut read = 0;
4658 let mut write = 0;
4659 while read + 4 <= input_len {
4660 let chunk = read_quad(buffer, read)?;
4661 let available = buffer.len();
4662 let output_tail = buffer.get_mut(write..).ok_or(DecodeError::OutputTooSmall {
4663 required: write,
4664 available,
4665 })?;
4666 let written = decode_chunk::<A, PAD>(chunk, output_tail)
4667 .map_err(|err| err.with_index_offset(read))?;
4668 read += 4;
4669 write += written;
4670 if written < 3 {
4671 if read != input_len {
4672 return Err(DecodeError::InvalidPadding { index: read - 4 });
4673 }
4674 return Ok(write);
4675 }
4676 }
4677
4678 let rem = input_len - read;
4679 if rem == 0 {
4680 return Ok(write);
4681 }
4682 if PAD {
4683 return Err(DecodeError::InvalidLength);
4684 }
4685 let mut tail = [0u8; 3];
4686 tail[..rem].copy_from_slice(&buffer[read..input_len]);
4687 decode_tail_unpadded::<A>(&tail[..rem], &mut buffer[write..])
4688 .map_err(|err| err.with_index_offset(read))
4689 .map(|n| write + n)
4690 }
4691}
4692
4693fn write_wrapped_bytes(
4694 input: &[u8],
4695 output: &mut [u8],
4696 output_offset: &mut usize,
4697 column: &mut usize,
4698 wrap: LineWrap,
4699) {
4700 for byte in input {
4701 write_wrapped_byte(*byte, output, output_offset, column, wrap);
4702 }
4703}
4704
4705fn write_wrapped_byte(
4706 byte: u8,
4707 output: &mut [u8],
4708 output_offset: &mut usize,
4709 column: &mut usize,
4710 wrap: LineWrap,
4711) {
4712 if *column == wrap.line_len {
4713 let line_ending = wrap.line_ending.as_bytes();
4714 let mut index = 0;
4715 while index < line_ending.len() {
4716 output[*output_offset] = line_ending[index];
4717 *output_offset += 1;
4718 index += 1;
4719 }
4720 *column = 0;
4721 }
4722
4723 output[*output_offset] = byte;
4724 *output_offset += 1;
4725 *column += 1;
4726}
4727
4728#[derive(Clone, Copy, Debug, Eq, PartialEq)]
4730pub enum EncodeError {
4731 LengthOverflow,
4733 InvalidLineWrap {
4735 line_len: usize,
4737 },
4738 InputTooLarge {
4740 input_len: usize,
4742 buffer_len: usize,
4744 },
4745 OutputTooSmall {
4747 required: usize,
4749 available: usize,
4751 },
4752}
4753
4754impl core::fmt::Display for EncodeError {
4755 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
4756 match self {
4757 Self::LengthOverflow => f.write_str("base64 output length overflows usize"),
4758 Self::InvalidLineWrap { line_len } => {
4759 write!(f, "base64 line wrap length {line_len} is invalid")
4760 }
4761 Self::InputTooLarge {
4762 input_len,
4763 buffer_len,
4764 } => write!(
4765 f,
4766 "base64 input length {input_len} exceeds buffer length {buffer_len}"
4767 ),
4768 Self::OutputTooSmall {
4769 required,
4770 available,
4771 } => write!(
4772 f,
4773 "base64 output buffer too small: required {required}, available {available}"
4774 ),
4775 }
4776 }
4777}
4778
4779#[cfg(feature = "std")]
4780impl std::error::Error for EncodeError {}
4781
4782#[derive(Clone, Copy, Debug, Eq, PartialEq)]
4784pub enum AlphabetError {
4785 InvalidByte {
4787 index: usize,
4789 byte: u8,
4791 },
4792 PaddingByte {
4794 index: usize,
4796 },
4797 DuplicateByte {
4799 first: usize,
4801 second: usize,
4803 byte: u8,
4805 },
4806}
4807
4808impl core::fmt::Display for AlphabetError {
4809 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
4810 match self {
4811 Self::InvalidByte { index, byte } => {
4812 write!(
4813 f,
4814 "invalid base64 alphabet byte 0x{byte:02x} at index {index}"
4815 )
4816 }
4817 Self::PaddingByte { index } => {
4818 write!(f, "base64 alphabet contains padding byte at index {index}")
4819 }
4820 Self::DuplicateByte {
4821 first,
4822 second,
4823 byte,
4824 } => write!(
4825 f,
4826 "base64 alphabet byte 0x{byte:02x} is duplicated at indexes {first} and {second}"
4827 ),
4828 }
4829 }
4830}
4831
4832#[cfg(feature = "std")]
4833impl std::error::Error for AlphabetError {}
4834
4835#[derive(Clone, Copy, Debug, Eq, PartialEq)]
4837pub enum DecodeError {
4838 InvalidInput,
4841 InvalidLength,
4843 InvalidByte {
4845 index: usize,
4847 byte: u8,
4849 },
4850 InvalidPadding {
4852 index: usize,
4854 },
4855 InvalidLineWrap {
4857 index: usize,
4859 },
4860 OutputTooSmall {
4862 required: usize,
4864 available: usize,
4866 },
4867}
4868
4869impl core::fmt::Display for DecodeError {
4870 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
4871 match self {
4872 Self::InvalidInput => f.write_str("malformed base64 input"),
4873 Self::InvalidLength => f.write_str("invalid base64 input length"),
4874 Self::InvalidByte { index, byte } => {
4875 write!(f, "invalid base64 byte 0x{byte:02x} at index {index}")
4876 }
4877 Self::InvalidPadding { index } => write!(f, "invalid base64 padding at index {index}"),
4878 Self::InvalidLineWrap { index } => {
4879 write!(f, "invalid base64 line wrapping at index {index}")
4880 }
4881 Self::OutputTooSmall {
4882 required,
4883 available,
4884 } => write!(
4885 f,
4886 "base64 decode output buffer too small: required {required}, available {available}"
4887 ),
4888 }
4889 }
4890}
4891
4892impl DecodeError {
4893 fn with_index_offset(self, offset: usize) -> Self {
4894 match self {
4895 Self::InvalidByte { index, byte } => Self::InvalidByte {
4896 index: index + offset,
4897 byte,
4898 },
4899 Self::InvalidPadding { index } => Self::InvalidPadding {
4900 index: index + offset,
4901 },
4902 Self::InvalidLineWrap { index } => Self::InvalidLineWrap {
4903 index: index + offset,
4904 },
4905 Self::InvalidInput | Self::InvalidLength | Self::OutputTooSmall { .. } => self,
4906 }
4907 }
4908}
4909
4910#[cfg(feature = "std")]
4911impl std::error::Error for DecodeError {}
4912
4913fn validate_legacy_decode<A: Alphabet, const PAD: bool>(
4914 input: &[u8],
4915) -> Result<usize, DecodeError> {
4916 let mut chunk = [0u8; 4];
4917 let mut indexes = [0usize; 4];
4918 let mut chunk_len = 0;
4919 let mut required = 0;
4920 let mut terminal_seen = false;
4921
4922 for (index, byte) in input.iter().copied().enumerate() {
4923 if is_legacy_whitespace(byte) {
4924 continue;
4925 }
4926 if terminal_seen {
4927 return Err(DecodeError::InvalidPadding { index });
4928 }
4929
4930 chunk[chunk_len] = byte;
4931 indexes[chunk_len] = index;
4932 chunk_len += 1;
4933
4934 if chunk_len == 4 {
4935 let written =
4936 validate_chunk::<A, PAD>(chunk).map_err(|err| map_chunk_error(err, &indexes))?;
4937 required += written;
4938 terminal_seen = written < 3;
4939 chunk_len = 0;
4940 }
4941 }
4942
4943 if chunk_len == 0 {
4944 return Ok(required);
4945 }
4946 if PAD {
4947 return Err(DecodeError::InvalidLength);
4948 }
4949
4950 validate_tail_unpadded::<A>(&chunk[..chunk_len])
4951 .map_err(|err| map_partial_chunk_error(err, &indexes, chunk_len))?;
4952 Ok(required + decoded_capacity(chunk_len))
4953}
4954
4955fn decode_legacy_to_slice<A: Alphabet, const PAD: bool>(
4956 input: &[u8],
4957 output: &mut [u8],
4958) -> Result<usize, DecodeError> {
4959 let mut chunk = [0u8; 4];
4960 let mut indexes = [0usize; 4];
4961 let mut chunk_len = 0;
4962 let mut write = 0;
4963 let mut terminal_seen = false;
4964
4965 for (index, byte) in input.iter().copied().enumerate() {
4966 if is_legacy_whitespace(byte) {
4967 continue;
4968 }
4969 if terminal_seen {
4970 return Err(DecodeError::InvalidPadding { index });
4971 }
4972
4973 chunk[chunk_len] = byte;
4974 indexes[chunk_len] = index;
4975 chunk_len += 1;
4976
4977 if chunk_len == 4 {
4978 let available = output.len();
4979 let output_tail = output.get_mut(write..).ok_or(DecodeError::OutputTooSmall {
4980 required: write,
4981 available,
4982 })?;
4983 let written = decode_chunk::<A, PAD>(chunk, output_tail)
4984 .map_err(|err| map_chunk_error(err, &indexes))?;
4985 write += written;
4986 terminal_seen = written < 3;
4987 chunk_len = 0;
4988 }
4989 }
4990
4991 if chunk_len == 0 {
4992 return Ok(write);
4993 }
4994 if PAD {
4995 return Err(DecodeError::InvalidLength);
4996 }
4997
4998 decode_tail_unpadded::<A>(&chunk[..chunk_len], &mut output[write..])
4999 .map_err(|err| map_partial_chunk_error(err, &indexes, chunk_len))
5000 .map(|n| write + n)
5001}
5002
5003struct WrappedBytes<'a> {
5004 input: &'a [u8],
5005 wrap: LineWrap,
5006 index: usize,
5007 line_len: usize,
5008}
5009
5010impl<'a> WrappedBytes<'a> {
5011 const fn new(input: &'a [u8], wrap: LineWrap) -> Result<Self, DecodeError> {
5012 if wrap.line_len == 0 {
5013 return Err(DecodeError::InvalidLineWrap { index: 0 });
5014 }
5015 Ok(Self {
5016 input,
5017 wrap,
5018 index: 0,
5019 line_len: 0,
5020 })
5021 }
5022
5023 fn next_byte(&mut self) -> Result<Option<(usize, u8)>, DecodeError> {
5024 loop {
5025 if self.index == self.input.len() {
5026 return Ok(None);
5027 }
5028
5029 if self.starts_with_line_ending() {
5030 let line_end_index = self.index;
5031 if self.line_len == 0 {
5032 return Err(DecodeError::InvalidLineWrap {
5033 index: line_end_index,
5034 });
5035 }
5036
5037 self.index += self.wrap.line_ending.byte_len();
5038 if self.index == self.input.len() {
5039 self.line_len = 0;
5040 return Ok(None);
5041 }
5042
5043 if self.line_len != self.wrap.line_len {
5044 return Err(DecodeError::InvalidLineWrap {
5045 index: line_end_index,
5046 });
5047 }
5048 self.line_len = 0;
5049 continue;
5050 }
5051
5052 let byte = self.input[self.index];
5053 if matches!(byte, b'\r' | b'\n') {
5054 return Err(DecodeError::InvalidLineWrap { index: self.index });
5055 }
5056
5057 self.line_len += 1;
5058 if self.line_len > self.wrap.line_len {
5059 return Err(DecodeError::InvalidLineWrap { index: self.index });
5060 }
5061
5062 let index = self.index;
5063 self.index += 1;
5064 return Ok(Some((index, byte)));
5065 }
5066 }
5067
5068 fn starts_with_line_ending(&self) -> bool {
5069 let line_ending = self.wrap.line_ending.as_bytes();
5070 let end = self.index + line_ending.len();
5071 end <= self.input.len() && &self.input[self.index..end] == line_ending
5072 }
5073}
5074
5075fn validate_wrapped_decode<A: Alphabet, const PAD: bool>(
5076 input: &[u8],
5077 wrap: LineWrap,
5078) -> Result<usize, DecodeError> {
5079 let mut bytes = WrappedBytes::new(input, wrap)?;
5080 let mut chunk = [0u8; 4];
5081 let mut indexes = [0usize; 4];
5082 let mut chunk_len = 0;
5083 let mut required = 0;
5084 let mut terminal_seen = false;
5085
5086 while let Some((index, byte)) = bytes.next_byte()? {
5087 if terminal_seen {
5088 return Err(DecodeError::InvalidPadding { index });
5089 }
5090
5091 chunk[chunk_len] = byte;
5092 indexes[chunk_len] = index;
5093 chunk_len += 1;
5094
5095 if chunk_len == 4 {
5096 let written =
5097 validate_chunk::<A, PAD>(chunk).map_err(|err| map_chunk_error(err, &indexes))?;
5098 required += written;
5099 terminal_seen = written < 3;
5100 chunk_len = 0;
5101 }
5102 }
5103
5104 if chunk_len == 0 {
5105 return Ok(required);
5106 }
5107 if PAD {
5108 return Err(DecodeError::InvalidLength);
5109 }
5110
5111 validate_tail_unpadded::<A>(&chunk[..chunk_len])
5112 .map_err(|err| map_partial_chunk_error(err, &indexes, chunk_len))?;
5113 Ok(required + decoded_capacity(chunk_len))
5114}
5115
5116fn decode_wrapped_to_slice<A: Alphabet, const PAD: bool>(
5117 input: &[u8],
5118 output: &mut [u8],
5119 wrap: LineWrap,
5120) -> Result<usize, DecodeError> {
5121 let mut bytes = WrappedBytes::new(input, wrap)?;
5122 let mut chunk = [0u8; 4];
5123 let mut indexes = [0usize; 4];
5124 let mut chunk_len = 0;
5125 let mut write = 0;
5126 let mut terminal_seen = false;
5127
5128 while let Some((index, byte)) = bytes.next_byte()? {
5129 if terminal_seen {
5130 return Err(DecodeError::InvalidPadding { index });
5131 }
5132
5133 chunk[chunk_len] = byte;
5134 indexes[chunk_len] = index;
5135 chunk_len += 1;
5136
5137 if chunk_len == 4 {
5138 let available = output.len();
5139 let output_tail = output.get_mut(write..).ok_or(DecodeError::OutputTooSmall {
5140 required: write,
5141 available,
5142 })?;
5143 let written = decode_chunk::<A, PAD>(chunk, output_tail)
5144 .map_err(|err| map_chunk_error(err, &indexes))?;
5145 write += written;
5146 terminal_seen = written < 3;
5147 chunk_len = 0;
5148 }
5149 }
5150
5151 if chunk_len == 0 {
5152 return Ok(write);
5153 }
5154 if PAD {
5155 return Err(DecodeError::InvalidLength);
5156 }
5157
5158 decode_tail_unpadded::<A>(&chunk[..chunk_len], &mut output[write..])
5159 .map_err(|err| map_partial_chunk_error(err, &indexes, chunk_len))
5160 .map(|n| write + n)
5161}
5162
5163fn compact_wrapped_input(buffer: &mut [u8], wrap: LineWrap) -> Result<usize, DecodeError> {
5164 if !wrap.is_valid() {
5165 return Err(DecodeError::InvalidLineWrap { index: 0 });
5166 }
5167
5168 let line_ending = wrap.line_ending.as_bytes();
5169 let line_ending_len = line_ending.len();
5170 let mut read = 0;
5171 let mut write = 0;
5172
5173 while read < buffer.len() {
5174 let line_end = read + line_ending_len;
5175 if buffer.get(read..line_end) == Some(line_ending) {
5176 read = line_end;
5177 continue;
5178 }
5179
5180 buffer[write] = buffer[read];
5181 write += 1;
5182 read += 1;
5183 }
5184
5185 Ok(write)
5186}
5187
5188#[inline]
5189const fn is_legacy_whitespace(byte: u8) -> bool {
5190 matches!(byte, b' ' | b'\t' | b'\r' | b'\n')
5191}
5192
5193fn map_chunk_error(err: DecodeError, indexes: &[usize; 4]) -> DecodeError {
5194 match err {
5195 DecodeError::InvalidByte { index, byte } => DecodeError::InvalidByte {
5196 index: indexes[index],
5197 byte,
5198 },
5199 DecodeError::InvalidPadding { index } => DecodeError::InvalidPadding {
5200 index: indexes[index],
5201 },
5202 DecodeError::InvalidInput
5203 | DecodeError::InvalidLineWrap { .. }
5204 | DecodeError::InvalidLength
5205 | DecodeError::OutputTooSmall { .. } => err,
5206 }
5207}
5208
5209fn map_partial_chunk_error(err: DecodeError, indexes: &[usize; 4], len: usize) -> DecodeError {
5210 match err {
5211 DecodeError::InvalidByte { index, byte } if index < len => DecodeError::InvalidByte {
5212 index: indexes[index],
5213 byte,
5214 },
5215 DecodeError::InvalidPadding { index } if index < len => DecodeError::InvalidPadding {
5216 index: indexes[index],
5217 },
5218 DecodeError::InvalidByte { .. }
5219 | DecodeError::InvalidPadding { .. }
5220 | DecodeError::InvalidLineWrap { .. }
5221 | DecodeError::InvalidInput
5222 | DecodeError::InvalidLength
5223 | DecodeError::OutputTooSmall { .. } => err,
5224 }
5225}
5226
5227fn decode_padded<A: Alphabet>(input: &[u8], output: &mut [u8]) -> Result<usize, DecodeError> {
5228 if !input.len().is_multiple_of(4) {
5229 return Err(DecodeError::InvalidLength);
5230 }
5231 let required = decoded_len_padded(input)?;
5232 if output.len() < required {
5233 return Err(DecodeError::OutputTooSmall {
5234 required,
5235 available: output.len(),
5236 });
5237 }
5238
5239 let mut read = 0;
5240 let mut write = 0;
5241 while read < input.len() {
5242 let chunk = read_quad(input, read)?;
5243 let available = output.len();
5244 let output_tail = output.get_mut(write..).ok_or(DecodeError::OutputTooSmall {
5245 required: write,
5246 available,
5247 })?;
5248 let written = decode_chunk::<A, true>(chunk, output_tail)
5249 .map_err(|err| err.with_index_offset(read))?;
5250 read += 4;
5251 write += written;
5252 if written < 3 && read != input.len() {
5253 return Err(DecodeError::InvalidPadding { index: read - 4 });
5254 }
5255 }
5256 Ok(write)
5257}
5258
5259fn validate_decode<A: Alphabet, const PAD: bool>(input: &[u8]) -> Result<usize, DecodeError> {
5260 if input.is_empty() {
5261 return Ok(0);
5262 }
5263
5264 if PAD {
5265 validate_padded::<A>(input)
5266 } else {
5267 validate_unpadded::<A>(input)
5268 }
5269}
5270
5271fn validate_padded<A: Alphabet>(input: &[u8]) -> Result<usize, DecodeError> {
5272 if !input.len().is_multiple_of(4) {
5273 return Err(DecodeError::InvalidLength);
5274 }
5275 let required = decoded_len_padded(input)?;
5276
5277 let mut read = 0;
5278 while read < input.len() {
5279 let chunk = read_quad(input, read)?;
5280 let written =
5281 validate_chunk::<A, true>(chunk).map_err(|err| err.with_index_offset(read))?;
5282 read += 4;
5283 if written < 3 && read != input.len() {
5284 return Err(DecodeError::InvalidPadding { index: read - 4 });
5285 }
5286 }
5287
5288 Ok(required)
5289}
5290
5291fn validate_unpadded<A: Alphabet>(input: &[u8]) -> Result<usize, DecodeError> {
5292 let required = decoded_len_unpadded(input)?;
5293
5294 let mut read = 0;
5295 while read + 4 <= input.len() {
5296 let chunk = read_quad(input, read)?;
5297 validate_chunk::<A, false>(chunk).map_err(|err| err.with_index_offset(read))?;
5298 read += 4;
5299 }
5300 validate_tail_unpadded::<A>(&input[read..]).map_err(|err| err.with_index_offset(read))?;
5301
5302 Ok(required)
5303}
5304
5305fn decode_unpadded<A: Alphabet>(input: &[u8], output: &mut [u8]) -> Result<usize, DecodeError> {
5306 let required = decoded_len_unpadded(input)?;
5307 if output.len() < required {
5308 return Err(DecodeError::OutputTooSmall {
5309 required,
5310 available: output.len(),
5311 });
5312 }
5313
5314 let mut read = 0;
5315 let mut write = 0;
5316 while read + 4 <= input.len() {
5317 let chunk = read_quad(input, read)?;
5318 let available = output.len();
5319 let output_tail = output.get_mut(write..).ok_or(DecodeError::OutputTooSmall {
5320 required: write,
5321 available,
5322 })?;
5323 let written = decode_chunk::<A, false>(chunk, output_tail)
5324 .map_err(|err| err.with_index_offset(read))?;
5325 read += 4;
5326 write += written;
5327 }
5328 decode_tail_unpadded::<A>(&input[read..], &mut output[write..])
5329 .map_err(|err| err.with_index_offset(read))
5330 .map(|n| write + n)
5331}
5332
5333fn decoded_len_padded(input: &[u8]) -> Result<usize, DecodeError> {
5334 if input.is_empty() {
5335 return Ok(0);
5336 }
5337 if !input.len().is_multiple_of(4) {
5338 return Err(DecodeError::InvalidLength);
5339 }
5340 let mut padding = 0;
5341 if input[input.len() - 1] == b'=' {
5342 padding += 1;
5343 }
5344 if input[input.len() - 2] == b'=' {
5345 padding += 1;
5346 }
5347 if padding == 0
5348 && let Some(index) = input.iter().position(|byte| *byte == b'=')
5349 {
5350 return Err(DecodeError::InvalidPadding { index });
5351 }
5352 if padding > 0 {
5353 let first_pad = input.len() - padding;
5354 if let Some(index) = input[..first_pad].iter().position(|byte| *byte == b'=') {
5355 return Err(DecodeError::InvalidPadding { index });
5356 }
5357 }
5358 Ok(input.len() / 4 * 3 - padding)
5359}
5360
5361fn decoded_len_unpadded(input: &[u8]) -> Result<usize, DecodeError> {
5362 if input.len() % 4 == 1 {
5363 return Err(DecodeError::InvalidLength);
5364 }
5365 if let Some(index) = input.iter().position(|byte| *byte == b'=') {
5366 return Err(DecodeError::InvalidPadding { index });
5367 }
5368 Ok(decoded_capacity(input.len()))
5369}
5370
5371fn read_quad(input: &[u8], offset: usize) -> Result<[u8; 4], DecodeError> {
5372 let end = offset.checked_add(4).ok_or(DecodeError::InvalidLength)?;
5373 match input.get(offset..end) {
5374 Some([b0, b1, b2, b3]) => Ok([*b0, *b1, *b2, *b3]),
5375 _ => Err(DecodeError::InvalidLength),
5376 }
5377}
5378
5379fn first_padding_index(input: [u8; 4]) -> usize {
5380 let [b0, b1, b2, b3] = input;
5381 if b0 == b'=' {
5382 0
5383 } else if b1 == b'=' {
5384 1
5385 } else if b2 == b'=' {
5386 2
5387 } else if b3 == b'=' {
5388 3
5389 } else {
5390 0
5391 }
5392}
5393
5394fn validate_chunk<A: Alphabet, const PAD: bool>(input: [u8; 4]) -> Result<usize, DecodeError> {
5395 let [b0, b1, b2, b3] = input;
5396 let _v0 = decode_byte::<A>(b0, 0)?;
5397 let v1 = decode_byte::<A>(b1, 1)?;
5398
5399 match (b2, b3) {
5400 (b'=', b'=') if PAD => {
5401 if v1 & 0b0000_1111 != 0 {
5402 return Err(DecodeError::InvalidPadding { index: 1 });
5403 }
5404 Ok(1)
5405 }
5406 (b'=', _) if PAD => Err(DecodeError::InvalidPadding { index: 2 }),
5407 (_, b'=') if PAD => {
5408 let v2 = decode_byte::<A>(b2, 2)?;
5409 if v2 & 0b0000_0011 != 0 {
5410 return Err(DecodeError::InvalidPadding { index: 2 });
5411 }
5412 Ok(2)
5413 }
5414 (b'=', _) | (_, b'=') => Err(DecodeError::InvalidPadding {
5415 index: first_padding_index(input),
5416 }),
5417 _ => {
5418 decode_byte::<A>(b2, 2)?;
5419 decode_byte::<A>(b3, 3)?;
5420 Ok(3)
5421 }
5422 }
5423}
5424
5425fn decode_chunk<A: Alphabet, const PAD: bool>(
5426 input: [u8; 4],
5427 output: &mut [u8],
5428) -> Result<usize, DecodeError> {
5429 let [b0, b1, b2, b3] = input;
5430 let v0 = decode_byte::<A>(b0, 0)?;
5431 let v1 = decode_byte::<A>(b1, 1)?;
5432
5433 match (b2, b3) {
5434 (b'=', b'=') if PAD => {
5435 if output.is_empty() {
5436 return Err(DecodeError::OutputTooSmall {
5437 required: 1,
5438 available: output.len(),
5439 });
5440 }
5441 if v1 & 0b0000_1111 != 0 {
5442 return Err(DecodeError::InvalidPadding { index: 1 });
5443 }
5444 output[0] = (v0 << 2) | (v1 >> 4);
5445 Ok(1)
5446 }
5447 (b'=', _) if PAD => Err(DecodeError::InvalidPadding { index: 2 }),
5448 (_, b'=') if PAD => {
5449 if output.len() < 2 {
5450 return Err(DecodeError::OutputTooSmall {
5451 required: 2,
5452 available: output.len(),
5453 });
5454 }
5455 let v2 = decode_byte::<A>(b2, 2)?;
5456 if v2 & 0b0000_0011 != 0 {
5457 return Err(DecodeError::InvalidPadding { index: 2 });
5458 }
5459 output[0] = (v0 << 2) | (v1 >> 4);
5460 output[1] = (v1 << 4) | (v2 >> 2);
5461 Ok(2)
5462 }
5463 (b'=', _) | (_, b'=') => Err(DecodeError::InvalidPadding {
5464 index: first_padding_index(input),
5465 }),
5466 _ => {
5467 if output.len() < 3 {
5468 return Err(DecodeError::OutputTooSmall {
5469 required: 3,
5470 available: output.len(),
5471 });
5472 }
5473 let v2 = decode_byte::<A>(b2, 2)?;
5474 let v3 = decode_byte::<A>(b3, 3)?;
5475 output[0] = (v0 << 2) | (v1 >> 4);
5476 output[1] = (v1 << 4) | (v2 >> 2);
5477 output[2] = (v2 << 6) | v3;
5478 Ok(3)
5479 }
5480 }
5481}
5482
5483fn validate_tail_unpadded<A: Alphabet>(input: &[u8]) -> Result<(), DecodeError> {
5484 match input.len() {
5485 0 => Ok(()),
5486 2 => {
5487 decode_byte::<A>(input[0], 0)?;
5488 let v1 = decode_byte::<A>(input[1], 1)?;
5489 if v1 & 0b0000_1111 != 0 {
5490 return Err(DecodeError::InvalidPadding { index: 1 });
5491 }
5492 Ok(())
5493 }
5494 3 => {
5495 decode_byte::<A>(input[0], 0)?;
5496 decode_byte::<A>(input[1], 1)?;
5497 let v2 = decode_byte::<A>(input[2], 2)?;
5498 if v2 & 0b0000_0011 != 0 {
5499 return Err(DecodeError::InvalidPadding { index: 2 });
5500 }
5501 Ok(())
5502 }
5503 _ => Err(DecodeError::InvalidLength),
5504 }
5505}
5506
5507fn decode_tail_unpadded<A: Alphabet>(
5508 input: &[u8],
5509 output: &mut [u8],
5510) -> Result<usize, DecodeError> {
5511 match input.len() {
5512 0 => Ok(0),
5513 2 => {
5514 if output.is_empty() {
5515 return Err(DecodeError::OutputTooSmall {
5516 required: 1,
5517 available: output.len(),
5518 });
5519 }
5520 let v0 = decode_byte::<A>(input[0], 0)?;
5521 let v1 = decode_byte::<A>(input[1], 1)?;
5522 if v1 & 0b0000_1111 != 0 {
5523 return Err(DecodeError::InvalidPadding { index: 1 });
5524 }
5525 output[0] = (v0 << 2) | (v1 >> 4);
5526 Ok(1)
5527 }
5528 3 => {
5529 if output.len() < 2 {
5530 return Err(DecodeError::OutputTooSmall {
5531 required: 2,
5532 available: output.len(),
5533 });
5534 }
5535 let v0 = decode_byte::<A>(input[0], 0)?;
5536 let v1 = decode_byte::<A>(input[1], 1)?;
5537 let v2 = decode_byte::<A>(input[2], 2)?;
5538 if v2 & 0b0000_0011 != 0 {
5539 return Err(DecodeError::InvalidPadding { index: 2 });
5540 }
5541 output[0] = (v0 << 2) | (v1 >> 4);
5542 output[1] = (v1 << 4) | (v2 >> 2);
5543 Ok(2)
5544 }
5545 _ => Err(DecodeError::InvalidLength),
5546 }
5547}
5548
5549fn decode_byte<A: Alphabet>(byte: u8, index: usize) -> Result<u8, DecodeError> {
5550 A::decode(byte).ok_or(DecodeError::InvalidByte { index, byte })
5551}
5552
5553fn ct_decode_slice<A: Alphabet, const PAD: bool>(
5554 input: &[u8],
5555 output: &mut [u8],
5556) -> Result<usize, DecodeError> {
5557 if input.is_empty() {
5558 return Ok(0);
5559 }
5560
5561 if PAD {
5562 ct_decode_padded::<A>(input, output)
5563 } else {
5564 ct_decode_unpadded::<A>(input, output)
5565 }
5566}
5567
5568fn ct_decode_in_place<A: Alphabet, const PAD: bool>(
5569 buffer: &mut [u8],
5570) -> Result<usize, DecodeError> {
5571 if buffer.is_empty() {
5572 return Ok(0);
5573 }
5574
5575 if PAD {
5576 ct_decode_padded_in_place::<A>(buffer)
5577 } else {
5578 ct_decode_unpadded_in_place::<A>(buffer)
5579 }
5580}
5581
5582fn ct_validate_decode<A: Alphabet, const PAD: bool>(input: &[u8]) -> Result<(), DecodeError> {
5583 if input.is_empty() {
5584 return Ok(());
5585 }
5586
5587 if PAD {
5588 ct_validate_padded::<A>(input)
5589 } else {
5590 ct_validate_unpadded::<A>(input)
5591 }
5592}
5593
5594fn ct_validate_padded<A: Alphabet>(input: &[u8]) -> Result<(), DecodeError> {
5595 if !input.len().is_multiple_of(4) {
5596 return Err(DecodeError::InvalidLength);
5597 }
5598
5599 let padding = ct_padding_len(input);
5600 let mut invalid_byte = 0u8;
5601 let mut invalid_padding = 0u8;
5602 let mut read = 0;
5603
5604 while read + 4 < input.len() {
5605 let [b0, b1, b2, b3] = read_quad(input, read)?;
5606 let (_, valid0) = ct_decode_alphabet_byte::<A>(b0);
5607 let (_, valid1) = ct_decode_alphabet_byte::<A>(b1);
5608 let (_, valid2) = ct_decode_alphabet_byte::<A>(b2);
5609 let (_, valid3) = ct_decode_alphabet_byte::<A>(b3);
5610
5611 invalid_byte |= !valid0;
5612 invalid_byte |= !valid1;
5613 invalid_byte |= !valid2;
5614 invalid_byte |= !valid3;
5615 invalid_padding |= ct_mask_eq_u8(b2, b'=');
5616 invalid_padding |= ct_mask_eq_u8(b3, b'=');
5617 read += 4;
5618 }
5619
5620 let final_chunk = read_quad(input, read)?;
5621 let (_, final_invalid_byte, final_invalid_padding, _) =
5622 ct_padded_final_quantum::<A>(final_chunk, padding);
5623 invalid_byte |= final_invalid_byte;
5624 invalid_padding |= final_invalid_padding;
5625
5626 report_ct_error(invalid_byte, invalid_padding)
5627}
5628
5629fn ct_validate_unpadded<A: Alphabet>(input: &[u8]) -> Result<(), DecodeError> {
5630 if input.len() % 4 == 1 {
5631 return Err(DecodeError::InvalidLength);
5632 }
5633
5634 let mut invalid_byte = 0u8;
5635 let mut invalid_padding = 0u8;
5636 let mut read = 0;
5637
5638 while read + 4 <= input.len() {
5639 let b0 = input[read];
5640 let b1 = input[read + 1];
5641 let b2 = input[read + 2];
5642 let b3 = input[read + 3];
5643 let (_, valid0) = ct_decode_alphabet_byte::<A>(b0);
5644 let (_, valid1) = ct_decode_alphabet_byte::<A>(b1);
5645 let (_, valid2) = ct_decode_alphabet_byte::<A>(b2);
5646 let (_, valid3) = ct_decode_alphabet_byte::<A>(b3);
5647
5648 invalid_byte |= !valid0;
5649 invalid_byte |= !valid1;
5650 invalid_byte |= !valid2;
5651 invalid_byte |= !valid3;
5652 invalid_padding |= ct_mask_eq_u8(b0, b'=');
5653 invalid_padding |= ct_mask_eq_u8(b1, b'=');
5654 invalid_padding |= ct_mask_eq_u8(b2, b'=');
5655 invalid_padding |= ct_mask_eq_u8(b3, b'=');
5656
5657 read += 4;
5658 }
5659
5660 match input.len() - read {
5661 0 => {}
5662 2 => {
5663 let b0 = input[read];
5664 let b1 = input[read + 1];
5665 let (_, valid0) = ct_decode_alphabet_byte::<A>(b0);
5666 let (v1, valid1) = ct_decode_alphabet_byte::<A>(b1);
5667 invalid_byte |= !valid0;
5668 invalid_byte |= !valid1;
5669 invalid_padding |= ct_mask_eq_u8(b0, b'=');
5670 invalid_padding |= ct_mask_eq_u8(b1, b'=');
5671 invalid_padding |= ct_mask_nonzero_u8(v1 & 0b0000_1111);
5672 }
5673 3 => {
5674 let b0 = input[read];
5675 let b1 = input[read + 1];
5676 let b2 = input[read + 2];
5677 let (_, valid0) = ct_decode_alphabet_byte::<A>(b0);
5678 let (_, valid1) = ct_decode_alphabet_byte::<A>(b1);
5679 let (v2, valid2) = ct_decode_alphabet_byte::<A>(b2);
5680 invalid_byte |= !valid0;
5681 invalid_byte |= !valid1;
5682 invalid_byte |= !valid2;
5683 invalid_padding |= ct_mask_eq_u8(b0, b'=');
5684 invalid_padding |= ct_mask_eq_u8(b1, b'=');
5685 invalid_padding |= ct_mask_eq_u8(b2, b'=');
5686 invalid_padding |= ct_mask_nonzero_u8(v2 & 0b0000_0011);
5687 }
5688 _ => return Err(DecodeError::InvalidLength),
5689 }
5690
5691 report_ct_error(invalid_byte, invalid_padding)
5692}
5693
5694fn ct_padded_final_quantum<A: Alphabet>(
5695 input: [u8; 4],
5696 padding: usize,
5697) -> ([u8; 3], u8, u8, usize) {
5698 let [b0, b1, b2, b3] = input;
5699 let (v0, valid0) = ct_decode_alphabet_byte::<A>(b0);
5700 let (v1, valid1) = ct_decode_alphabet_byte::<A>(b1);
5701 let (v2, valid2) = ct_decode_alphabet_byte::<A>(b2);
5702 let (v3, valid3) = ct_decode_alphabet_byte::<A>(b3);
5703
5704 let padding_byte = padding.to_le_bytes()[0];
5705 let no_padding = ct_mask_eq_u8(padding_byte, 0);
5706 let one_padding = ct_mask_eq_u8(padding_byte, 1);
5707 let two_padding = ct_mask_eq_u8(padding_byte, 2);
5708 let require_v2 = no_padding | one_padding;
5709 let require_v3 = no_padding;
5710
5711 let invalid_byte = !valid0 | !valid1 | (!valid2 & require_v2) | (!valid3 & require_v3);
5712 let invalid_padding = (ct_mask_nonzero_u8(v1 & 0b0000_1111) & two_padding)
5713 | ((ct_mask_eq_u8(b2, b'=') | ct_mask_nonzero_u8(v2 & 0b0000_0011)) & one_padding)
5714 | ((ct_mask_eq_u8(b2, b'=') | ct_mask_eq_u8(b3, b'=')) & no_padding);
5715
5716 (
5717 [(v0 << 2) | (v1 >> 4), (v1 << 4) | (v2 >> 2), (v2 << 6) | v3],
5718 invalid_byte,
5719 invalid_padding,
5720 3 - padding,
5721 )
5722}
5723
5724fn ct_decode_padded<A: Alphabet>(input: &[u8], output: &mut [u8]) -> Result<usize, DecodeError> {
5725 if !input.len().is_multiple_of(4) {
5726 return Err(DecodeError::InvalidLength);
5727 }
5728
5729 let padding = ct_padding_len(input);
5730 let required = input.len() / 4 * 3 - padding;
5731 if output.len() < required {
5732 return Err(DecodeError::OutputTooSmall {
5733 required,
5734 available: output.len(),
5735 });
5736 }
5737
5738 let mut invalid_byte = 0u8;
5739 let mut invalid_padding = 0u8;
5740 let mut write = 0;
5741 let mut read = 0;
5742
5743 while read + 4 < input.len() {
5744 let [b0, b1, b2, b3] = read_quad(input, read)?;
5745 let (v0, valid0) = ct_decode_alphabet_byte::<A>(b0);
5746 let (v1, valid1) = ct_decode_alphabet_byte::<A>(b1);
5747 let (v2, valid2) = ct_decode_alphabet_byte::<A>(b2);
5748 let (v3, valid3) = ct_decode_alphabet_byte::<A>(b3);
5749
5750 invalid_byte |= !valid0;
5751 invalid_byte |= !valid1;
5752 invalid_byte |= !valid2;
5753 invalid_byte |= !valid3;
5754 invalid_padding |= ct_mask_eq_u8(b2, b'=');
5755 invalid_padding |= ct_mask_eq_u8(b3, b'=');
5756 output[write] = (v0 << 2) | (v1 >> 4);
5757 output[write + 1] = (v1 << 4) | (v2 >> 2);
5758 output[write + 2] = (v2 << 6) | v3;
5759 write += 3;
5760 read += 4;
5761 }
5762
5763 let final_chunk = read_quad(input, read)?;
5764 let (final_bytes, final_invalid_byte, final_invalid_padding, final_written) =
5765 ct_padded_final_quantum::<A>(final_chunk, padding);
5766 invalid_byte |= final_invalid_byte;
5767 invalid_padding |= final_invalid_padding;
5768 output[write..write + final_written].copy_from_slice(&final_bytes[..final_written]);
5769 write += final_written;
5770
5771 report_ct_error(invalid_byte, invalid_padding)?;
5772 Ok(write)
5773}
5774
5775fn ct_decode_padded_in_place<A: Alphabet>(buffer: &mut [u8]) -> Result<usize, DecodeError> {
5776 if !buffer.len().is_multiple_of(4) {
5777 return Err(DecodeError::InvalidLength);
5778 }
5779
5780 let padding = ct_padding_len(buffer);
5781 let required = buffer.len() / 4 * 3 - padding;
5782 debug_assert!(required <= buffer.len());
5783
5784 let mut invalid_byte = 0u8;
5785 let mut invalid_padding = 0u8;
5786 let mut write = 0;
5787 let mut read = 0;
5788
5789 while read + 4 < buffer.len() {
5790 let [b0, b1, b2, b3] = read_quad(buffer, read)?;
5791 let (v0, valid0) = ct_decode_alphabet_byte::<A>(b0);
5792 let (v1, valid1) = ct_decode_alphabet_byte::<A>(b1);
5793 let (v2, valid2) = ct_decode_alphabet_byte::<A>(b2);
5794 let (v3, valid3) = ct_decode_alphabet_byte::<A>(b3);
5795
5796 invalid_byte |= !valid0;
5797 invalid_byte |= !valid1;
5798 invalid_byte |= !valid2;
5799 invalid_byte |= !valid3;
5800 invalid_padding |= ct_mask_eq_u8(b2, b'=');
5801 invalid_padding |= ct_mask_eq_u8(b3, b'=');
5802 buffer[write] = (v0 << 2) | (v1 >> 4);
5803 buffer[write + 1] = (v1 << 4) | (v2 >> 2);
5804 buffer[write + 2] = (v2 << 6) | v3;
5805 write += 3;
5806 read += 4;
5807 }
5808
5809 let final_chunk = read_quad(buffer, read)?;
5810 let (final_bytes, final_invalid_byte, final_invalid_padding, final_written) =
5811 ct_padded_final_quantum::<A>(final_chunk, padding);
5812 invalid_byte |= final_invalid_byte;
5813 invalid_padding |= final_invalid_padding;
5814 buffer[write..write + final_written].copy_from_slice(&final_bytes[..final_written]);
5815 write += final_written;
5816
5817 debug_assert_eq!(write, required);
5818 report_ct_error(invalid_byte, invalid_padding)?;
5819 Ok(write)
5820}
5821
5822fn ct_decode_unpadded<A: Alphabet>(input: &[u8], output: &mut [u8]) -> Result<usize, DecodeError> {
5823 if input.len() % 4 == 1 {
5824 return Err(DecodeError::InvalidLength);
5825 }
5826
5827 let required = decoded_capacity(input.len());
5828 if output.len() < required {
5829 return Err(DecodeError::OutputTooSmall {
5830 required,
5831 available: output.len(),
5832 });
5833 }
5834
5835 let mut invalid_byte = 0u8;
5836 let mut invalid_padding = 0u8;
5837 let mut write = 0;
5838 let mut read = 0;
5839
5840 while read + 4 <= input.len() {
5841 let b0 = input[read];
5842 let b1 = input[read + 1];
5843 let b2 = input[read + 2];
5844 let b3 = input[read + 3];
5845 let (v0, valid0) = ct_decode_alphabet_byte::<A>(b0);
5846 let (v1, valid1) = ct_decode_alphabet_byte::<A>(b1);
5847 let (v2, valid2) = ct_decode_alphabet_byte::<A>(b2);
5848 let (v3, valid3) = ct_decode_alphabet_byte::<A>(b3);
5849
5850 invalid_byte |= !valid0;
5851 invalid_byte |= !valid1;
5852 invalid_byte |= !valid2;
5853 invalid_byte |= !valid3;
5854 invalid_padding |= ct_mask_eq_u8(b0, b'=');
5855 invalid_padding |= ct_mask_eq_u8(b1, b'=');
5856 invalid_padding |= ct_mask_eq_u8(b2, b'=');
5857 invalid_padding |= ct_mask_eq_u8(b3, b'=');
5858
5859 output[write] = (v0 << 2) | (v1 >> 4);
5860 output[write + 1] = (v1 << 4) | (v2 >> 2);
5861 output[write + 2] = (v2 << 6) | v3;
5862 read += 4;
5863 write += 3;
5864 }
5865
5866 match input.len() - read {
5867 0 => {}
5868 2 => {
5869 let b0 = input[read];
5870 let b1 = input[read + 1];
5871 let (v0, valid0) = ct_decode_alphabet_byte::<A>(b0);
5872 let (v1, valid1) = ct_decode_alphabet_byte::<A>(b1);
5873 invalid_byte |= !valid0;
5874 invalid_byte |= !valid1;
5875 invalid_padding |= ct_mask_eq_u8(b0, b'=');
5876 invalid_padding |= ct_mask_eq_u8(b1, b'=');
5877 invalid_padding |= ct_mask_nonzero_u8(v1 & 0b0000_1111);
5878 output[write] = (v0 << 2) | (v1 >> 4);
5879 write += 1;
5880 }
5881 3 => {
5882 let b0 = input[read];
5883 let b1 = input[read + 1];
5884 let b2 = input[read + 2];
5885 let (v0, valid0) = ct_decode_alphabet_byte::<A>(b0);
5886 let (v1, valid1) = ct_decode_alphabet_byte::<A>(b1);
5887 let (v2, valid2) = ct_decode_alphabet_byte::<A>(b2);
5888 invalid_byte |= !valid0;
5889 invalid_byte |= !valid1;
5890 invalid_byte |= !valid2;
5891 invalid_padding |= ct_mask_eq_u8(b0, b'=');
5892 invalid_padding |= ct_mask_eq_u8(b1, b'=');
5893 invalid_padding |= ct_mask_eq_u8(b2, b'=');
5894 invalid_padding |= ct_mask_nonzero_u8(v2 & 0b0000_0011);
5895 output[write] = (v0 << 2) | (v1 >> 4);
5896 output[write + 1] = (v1 << 4) | (v2 >> 2);
5897 write += 2;
5898 }
5899 _ => return Err(DecodeError::InvalidLength),
5900 }
5901
5902 report_ct_error(invalid_byte, invalid_padding)?;
5903 Ok(write)
5904}
5905
5906fn ct_decode_unpadded_in_place<A: Alphabet>(buffer: &mut [u8]) -> Result<usize, DecodeError> {
5907 if buffer.len() % 4 == 1 {
5908 return Err(DecodeError::InvalidLength);
5909 }
5910
5911 let required = decoded_capacity(buffer.len());
5912 debug_assert!(required <= buffer.len());
5913
5914 let mut invalid_byte = 0u8;
5915 let mut invalid_padding = 0u8;
5916 let mut write = 0;
5917 let mut read = 0;
5918
5919 while read + 4 <= buffer.len() {
5920 let b0 = buffer[read];
5921 let b1 = buffer[read + 1];
5922 let b2 = buffer[read + 2];
5923 let b3 = buffer[read + 3];
5924 let (v0, valid0) = ct_decode_alphabet_byte::<A>(b0);
5925 let (v1, valid1) = ct_decode_alphabet_byte::<A>(b1);
5926 let (v2, valid2) = ct_decode_alphabet_byte::<A>(b2);
5927 let (v3, valid3) = ct_decode_alphabet_byte::<A>(b3);
5928
5929 invalid_byte |= !valid0;
5930 invalid_byte |= !valid1;
5931 invalid_byte |= !valid2;
5932 invalid_byte |= !valid3;
5933 invalid_padding |= ct_mask_eq_u8(b0, b'=');
5934 invalid_padding |= ct_mask_eq_u8(b1, b'=');
5935 invalid_padding |= ct_mask_eq_u8(b2, b'=');
5936 invalid_padding |= ct_mask_eq_u8(b3, b'=');
5937
5938 buffer[write] = (v0 << 2) | (v1 >> 4);
5939 buffer[write + 1] = (v1 << 4) | (v2 >> 2);
5940 buffer[write + 2] = (v2 << 6) | v3;
5941 read += 4;
5942 write += 3;
5943 }
5944
5945 match buffer.len() - read {
5946 0 => {}
5947 2 => {
5948 let b0 = buffer[read];
5949 let b1 = buffer[read + 1];
5950 let (v0, valid0) = ct_decode_alphabet_byte::<A>(b0);
5951 let (v1, valid1) = ct_decode_alphabet_byte::<A>(b1);
5952 invalid_byte |= !valid0;
5953 invalid_byte |= !valid1;
5954 invalid_padding |= ct_mask_eq_u8(b0, b'=');
5955 invalid_padding |= ct_mask_eq_u8(b1, b'=');
5956 invalid_padding |= ct_mask_nonzero_u8(v1 & 0b0000_1111);
5957 buffer[write] = (v0 << 2) | (v1 >> 4);
5958 write += 1;
5959 }
5960 3 => {
5961 let b0 = buffer[read];
5962 let b1 = buffer[read + 1];
5963 let b2 = buffer[read + 2];
5964 let (v0, valid0) = ct_decode_alphabet_byte::<A>(b0);
5965 let (v1, valid1) = ct_decode_alphabet_byte::<A>(b1);
5966 let (v2, valid2) = ct_decode_alphabet_byte::<A>(b2);
5967 invalid_byte |= !valid0;
5968 invalid_byte |= !valid1;
5969 invalid_byte |= !valid2;
5970 invalid_padding |= ct_mask_eq_u8(b0, b'=');
5971 invalid_padding |= ct_mask_eq_u8(b1, b'=');
5972 invalid_padding |= ct_mask_eq_u8(b2, b'=');
5973 invalid_padding |= ct_mask_nonzero_u8(v2 & 0b0000_0011);
5974 buffer[write] = (v0 << 2) | (v1 >> 4);
5975 buffer[write + 1] = (v1 << 4) | (v2 >> 2);
5976 write += 2;
5977 }
5978 _ => return Err(DecodeError::InvalidLength),
5979 }
5980
5981 debug_assert_eq!(write, required);
5982 report_ct_error(invalid_byte, invalid_padding)?;
5983 Ok(write)
5984}
5985
5986#[inline]
5987fn ct_decode_alphabet_byte<A: Alphabet>(byte: u8) -> (u8, u8) {
5988 let mut decoded = 0u8;
5989 let mut valid = 0u8;
5990 let mut candidate = 0u8;
5991
5992 while candidate < 64 {
5993 let matches = ct_mask_eq_u8(byte, A::ENCODE[candidate as usize]);
5994 decoded |= candidate & matches;
5995 valid |= matches;
5996 candidate += 1;
5997 }
5998
5999 (decoded, valid)
6000}
6001
6002fn ct_padding_len(input: &[u8]) -> usize {
6003 let last = input[input.len() - 1];
6004 let before_last = input[input.len() - 2];
6005 usize::from(ct_mask_eq_u8(last, b'=') & 1) + usize::from(ct_mask_eq_u8(before_last, b'=') & 1)
6006}
6007
6008fn report_ct_error(invalid_byte: u8, invalid_padding: u8) -> Result<(), DecodeError> {
6009 if (invalid_byte | invalid_padding) != 0 {
6010 Err(DecodeError::InvalidInput)
6011 } else {
6012 Ok(())
6013 }
6014}
6015
6016#[cfg(kani)]
6017mod kani_proofs {
6018 use super::{STANDARD, checked_encoded_len, ct, decoded_capacity};
6019
6020 #[kani::proof]
6021 fn checked_encoded_len_is_bounded_for_small_inputs() {
6022 let len = usize::from(kani::any::<u8>());
6023 let padded = kani::any::<bool>();
6024 let encoded = checked_encoded_len(len, padded).expect("u8 input length cannot overflow");
6025
6026 assert!(encoded >= len);
6027 assert!(encoded <= len / 3 * 4 + 4);
6028 }
6029
6030 #[kani::proof]
6031 fn decoded_capacity_is_bounded_for_small_inputs() {
6032 let len = usize::from(kani::any::<u8>());
6033 let capacity = decoded_capacity(len);
6034
6035 assert!(capacity <= len / 4 * 3 + 2);
6036 }
6037
6038 #[kani::proof]
6039 #[kani::unwind(3)]
6040 fn standard_in_place_decode_returns_prefix_within_buffer() {
6041 let mut buffer = kani::any::<[u8; 8]>();
6042 let result = STANDARD.decode_in_place(&mut buffer);
6043
6044 if let Ok(decoded) = result {
6045 assert!(decoded.len() <= 8);
6046 }
6047 }
6048
6049 #[kani::proof]
6050 #[kani::unwind(3)]
6051 fn standard_decode_slice_returns_written_within_output() {
6052 let input = kani::any::<[u8; 4]>();
6053 let mut output = kani::any::<[u8; 3]>();
6054 let result = STANDARD.decode_slice(&input, &mut output);
6055
6056 if let Ok(written) = result {
6057 assert!(written <= output.len());
6058 }
6059 }
6060
6061 #[kani::proof]
6062 #[kani::unwind(3)]
6063 fn standard_decode_slice_clear_tail_clears_output_on_error() {
6064 let input = kani::any::<[u8; 4]>();
6065 let mut output = kani::any::<[u8; 3]>();
6066 let result = STANDARD.decode_slice_clear_tail(&input, &mut output);
6067
6068 if result.is_err() {
6069 assert!(output.iter().all(|byte| *byte == 0));
6070 }
6071 }
6072
6073 #[kani::proof]
6074 #[kani::unwind(3)]
6075 fn standard_encode_slice_returns_written_within_output() {
6076 let input = kani::any::<[u8; 3]>();
6077 let mut output = kani::any::<[u8; 4]>();
6078 let result = STANDARD.encode_slice(&input, &mut output);
6079
6080 if let Ok(written) = result {
6081 assert!(written <= output.len());
6082 }
6083 }
6084
6085 #[kani::proof]
6086 #[kani::unwind(4)]
6087 fn standard_encode_in_place_returns_prefix_within_buffer() {
6088 let mut buffer = kani::any::<[u8; 8]>();
6089 let input_len = usize::from(kani::any::<u8>() % 9);
6090 let result = STANDARD.encode_in_place(&mut buffer, input_len);
6091
6092 if let Ok(encoded) = result {
6093 assert!(encoded.len() <= 8);
6094 }
6095 }
6096
6097 #[kani::proof]
6098 #[kani::unwind(3)]
6099 fn standard_clear_tail_decode_clears_buffer_on_error() {
6100 let mut buffer = kani::any::<[u8; 4]>();
6101 let result = STANDARD.decode_in_place_clear_tail(&mut buffer);
6102
6103 if result.is_err() {
6104 assert!(buffer.iter().all(|byte| *byte == 0));
6105 }
6106 }
6107
6108 #[kani::proof]
6109 #[kani::unwind(3)]
6110 fn ct_standard_decode_slice_returns_written_within_output() {
6111 let input = kani::any::<[u8; 4]>();
6112 let mut output = kani::any::<[u8; 3]>();
6113 let result = ct::STANDARD.decode_slice(&input, &mut output);
6114
6115 if let Ok(written) = result {
6116 assert!(written <= output.len());
6117 }
6118 }
6119
6120 #[kani::proof]
6121 #[kani::unwind(3)]
6122 fn ct_standard_decode_slice_clear_tail_clears_output_on_error() {
6123 let input = kani::any::<[u8; 4]>();
6124 let mut output = kani::any::<[u8; 3]>();
6125 let result = ct::STANDARD.decode_slice_clear_tail(&input, &mut output);
6126
6127 if result.is_err() {
6128 assert!(output.iter().all(|byte| *byte == 0));
6129 }
6130 }
6131
6132 #[kani::proof]
6133 #[kani::unwind(3)]
6134 fn ct_standard_decode_in_place_clear_tail_clears_buffer_on_error() {
6135 let mut buffer = kani::any::<[u8; 4]>();
6136 let result = ct::STANDARD.decode_in_place_clear_tail(&mut buffer);
6137
6138 if result.is_err() {
6139 assert!(buffer.iter().all(|byte| *byte == 0));
6140 }
6141 }
6142
6143 #[kani::proof]
6144 #[kani::unwind(3)]
6145 fn ct_standard_validate_matches_decode_for_one_quantum() {
6146 let input = kani::any::<[u8; 4]>();
6147 let mut output = kani::any::<[u8; 3]>();
6148
6149 let validate_ok = ct::STANDARD.validate_result(&input).is_ok();
6150 let decode_ok = ct::STANDARD.decode_slice(&input, &mut output).is_ok();
6151
6152 assert!(validate_ok == decode_ok);
6153 }
6154}
6155
6156#[cfg(test)]
6157mod tests {
6158 use super::*;
6159
6160 fn fill_pattern(output: &mut [u8], seed: usize) {
6161 for (index, byte) in output.iter_mut().enumerate() {
6162 let value = (index * 73 + seed * 19) % 256;
6163 *byte = u8::try_from(value).unwrap();
6164 }
6165 }
6166
6167 fn assert_encode_backend_matches_scalar<A, const PAD: bool>(input: &[u8])
6168 where
6169 A: Alphabet,
6170 {
6171 let engine = Engine::<A, PAD>::new();
6172 let mut dispatched = [0x55; 256];
6173 let mut scalar = [0xaa; 256];
6174
6175 let dispatched_result = engine.encode_slice(input, &mut dispatched);
6176 let scalar_result = backend::scalar_reference_encode_slice::<A, PAD>(input, &mut scalar);
6177
6178 assert_eq!(dispatched_result, scalar_result);
6179 if let Ok(written) = dispatched_result {
6180 assert_eq!(&dispatched[..written], &scalar[..written]);
6181 }
6182
6183 let required = checked_encoded_len(input.len(), PAD).unwrap();
6184 if required > 0 {
6185 let mut dispatched_short = [0x55; 256];
6186 let mut scalar_short = [0xaa; 256];
6187 let available = required - 1;
6188
6189 assert_eq!(
6190 engine.encode_slice(input, &mut dispatched_short[..available]),
6191 backend::scalar_reference_encode_slice::<A, PAD>(
6192 input,
6193 &mut scalar_short[..available],
6194 )
6195 );
6196 }
6197 }
6198
6199 fn assert_decode_backend_matches_scalar<A, const PAD: bool>(input: &[u8])
6200 where
6201 A: Alphabet,
6202 {
6203 let engine = Engine::<A, PAD>::new();
6204 let mut dispatched = [0x55; 128];
6205 let mut scalar = [0xaa; 128];
6206
6207 let dispatched_result = engine.decode_slice(input, &mut dispatched);
6208 let scalar_result = backend::scalar_reference_decode_slice::<A, PAD>(input, &mut scalar);
6209
6210 assert_eq!(dispatched_result, scalar_result);
6211 if let Ok(written) = dispatched_result {
6212 assert_eq!(&dispatched[..written], &scalar[..written]);
6213
6214 if written > 0 {
6215 let mut dispatched_short = [0x55; 128];
6216 let mut scalar_short = [0xaa; 128];
6217 let available = written - 1;
6218
6219 assert_eq!(
6220 engine.decode_slice(input, &mut dispatched_short[..available]),
6221 backend::scalar_reference_decode_slice::<A, PAD>(
6222 input,
6223 &mut scalar_short[..available],
6224 )
6225 );
6226 }
6227 }
6228 }
6229
6230 fn assert_backend_round_trip_matches_scalar<A, const PAD: bool>(input: &[u8])
6231 where
6232 A: Alphabet,
6233 {
6234 assert_encode_backend_matches_scalar::<A, PAD>(input);
6235
6236 let mut encoded = [0; 256];
6237 let encoded_len =
6238 backend::scalar_reference_encode_slice::<A, PAD>(input, &mut encoded).unwrap();
6239 assert_decode_backend_matches_scalar::<A, PAD>(&encoded[..encoded_len]);
6240 }
6241
6242 #[test]
6243 fn backend_dispatch_matches_scalar_reference_for_canonical_inputs() {
6244 let mut input = [0; 128];
6245
6246 for input_len in 0..=input.len() {
6247 fill_pattern(&mut input[..input_len], input_len);
6248 let input = &input[..input_len];
6249
6250 assert_backend_round_trip_matches_scalar::<Standard, true>(input);
6251 assert_backend_round_trip_matches_scalar::<Standard, false>(input);
6252 assert_backend_round_trip_matches_scalar::<UrlSafe, true>(input);
6253 assert_backend_round_trip_matches_scalar::<UrlSafe, false>(input);
6254 }
6255 }
6256
6257 #[test]
6258 fn backend_dispatch_matches_scalar_reference_for_malformed_inputs() {
6259 for input in [
6260 &b"Z"[..],
6261 b"====",
6262 b"AA=A",
6263 b"Zh==",
6264 b"Zm9=",
6265 b"Zm9v$g==",
6266 b"Zm9vZh==",
6267 ] {
6268 assert_decode_backend_matches_scalar::<Standard, true>(input);
6269 }
6270
6271 for input in [&b"Z"[..], b"AA=A", b"Zh", b"Zm9", b"Zm9vYg$"] {
6272 assert_decode_backend_matches_scalar::<Standard, false>(input);
6273 }
6274
6275 assert_decode_backend_matches_scalar::<UrlSafe, true>(b"AA+A");
6276 assert_decode_backend_matches_scalar::<UrlSafe, false>(b"AA/A");
6277 assert_decode_backend_matches_scalar::<Standard, true>(b"AA-A");
6278 assert_decode_backend_matches_scalar::<Standard, false>(b"AA_A");
6279 }
6280
6281 #[cfg(feature = "simd")]
6282 #[test]
6283 fn simd_dispatch_scaffold_keeps_scalar_active() {
6284 assert_eq!(simd::active_backend(), simd::ActiveBackend::Scalar);
6285 let _candidate = simd::detected_candidate();
6286 }
6287
6288 #[test]
6289 fn encodes_standard_vectors() {
6290 let vectors = [
6291 (&b""[..], &b""[..]),
6292 (&b"f"[..], &b"Zg=="[..]),
6293 (&b"fo"[..], &b"Zm8="[..]),
6294 (&b"foo"[..], &b"Zm9v"[..]),
6295 (&b"foob"[..], &b"Zm9vYg=="[..]),
6296 (&b"fooba"[..], &b"Zm9vYmE="[..]),
6297 (&b"foobar"[..], &b"Zm9vYmFy"[..]),
6298 ];
6299 for (input, expected) in vectors {
6300 let mut output = [0u8; 16];
6301 let written = STANDARD.encode_slice(input, &mut output).unwrap();
6302 assert_eq!(&output[..written], expected);
6303 }
6304 }
6305
6306 #[test]
6307 fn decodes_standard_vectors() {
6308 let vectors = [
6309 (&b""[..], &b""[..]),
6310 (&b"Zg=="[..], &b"f"[..]),
6311 (&b"Zm8="[..], &b"fo"[..]),
6312 (&b"Zm9v"[..], &b"foo"[..]),
6313 (&b"Zm9vYg=="[..], &b"foob"[..]),
6314 (&b"Zm9vYmE="[..], &b"fooba"[..]),
6315 (&b"Zm9vYmFy"[..], &b"foobar"[..]),
6316 ];
6317 for (input, expected) in vectors {
6318 let mut output = [0u8; 16];
6319 let written = STANDARD.decode_slice(input, &mut output).unwrap();
6320 assert_eq!(&output[..written], expected);
6321 }
6322 }
6323
6324 #[test]
6325 fn supports_unpadded_url_safe() {
6326 let mut encoded = [0u8; 16];
6327 let written = URL_SAFE_NO_PAD
6328 .encode_slice(b"\xfb\xff", &mut encoded)
6329 .unwrap();
6330 assert_eq!(&encoded[..written], b"-_8");
6331
6332 let mut decoded = [0u8; 2];
6333 let written = URL_SAFE_NO_PAD
6334 .decode_slice(&encoded[..written], &mut decoded)
6335 .unwrap();
6336 assert_eq!(&decoded[..written], b"\xfb\xff");
6337 }
6338
6339 #[test]
6340 fn decodes_in_place() {
6341 let mut buffer = *b"Zm9vYmFy";
6342 let decoded = STANDARD_NO_PAD.decode_in_place(&mut buffer).unwrap();
6343 assert_eq!(decoded, b"foobar");
6344 }
6345
6346 #[test]
6347 fn rejects_non_canonical_padding_bits() {
6348 let mut output = [0u8; 4];
6349 assert_eq!(
6350 STANDARD.decode_slice(b"Zh==", &mut output),
6351 Err(DecodeError::InvalidPadding { index: 1 })
6352 );
6353 assert_eq!(
6354 STANDARD.decode_slice(b"Zm9=", &mut output),
6355 Err(DecodeError::InvalidPadding { index: 2 })
6356 );
6357 }
6358}