Skip to main content

base64_ng/engine/
validate.rs

1use crate::{
2    Alphabet, DecodeError, EncodeError, Engine, LineWrap, checked_encoded_len,
3    checked_wrapped_encoded_len, decoded_len, encoded_len, validate_decode, validate_legacy_decode,
4    validate_wrapped_decode, wrapped_encoded_len,
5};
6
7impl<A, const PAD: bool> Engine<A, PAD>
8where
9    A: Alphabet,
10{
11    /// Returns the encoded length for this engine's padding policy.
12    pub const fn encoded_len(&self, input_len: usize) -> Result<usize, EncodeError> {
13        encoded_len(input_len, PAD)
14    }
15
16    /// Returns the encoded length for this engine, or `None` on overflow.
17    #[must_use]
18    pub const fn checked_encoded_len(&self, input_len: usize) -> Option<usize> {
19        checked_encoded_len(input_len, PAD)
20    }
21
22    /// Returns the encoded length after applying a line wrapping policy.
23    ///
24    /// The returned length includes inserted line endings but does not include
25    /// a trailing line ending after the final encoded line.
26    pub const fn wrapped_encoded_len(
27        &self,
28        input_len: usize,
29        wrap: LineWrap,
30    ) -> Result<usize, EncodeError> {
31        wrapped_encoded_len(input_len, PAD, wrap)
32    }
33
34    /// Returns the encoded length after line wrapping, or `None` on overflow or
35    /// invalid line wrapping.
36    #[must_use]
37    pub const fn checked_wrapped_encoded_len(
38        &self,
39        input_len: usize,
40        wrap: LineWrap,
41    ) -> Option<usize> {
42        checked_wrapped_encoded_len(input_len, PAD, wrap)
43    }
44
45    /// Returns the exact decoded length implied by input length and padding.
46    ///
47    /// This validates padding placement and impossible lengths, but it does not
48    /// validate alphabet membership or non-canonical trailing bits.
49    pub fn decoded_len(&self, input: &[u8]) -> Result<usize, DecodeError> {
50        decoded_len(input, PAD)
51    }
52
53    /// Returns the exact decoded length for the explicit legacy profile.
54    ///
55    /// The legacy profile ignores ASCII space, tab, carriage return, and line
56    /// feed bytes before applying the same alphabet, padding, and canonical-bit
57    /// checks as strict decoding.
58    pub fn decoded_len_legacy(&self, input: &[u8]) -> Result<usize, DecodeError> {
59        validate_legacy_decode::<A, PAD>(input)
60    }
61
62    /// Returns the exact decoded length for a line-wrapped profile.
63    ///
64    /// The wrapped profile accepts only the configured line ending. Non-final
65    /// lines must contain exactly `wrap.line_len` encoded bytes; the final line
66    /// may be shorter. A single trailing line ending after the final line is
67    /// accepted.
68    pub fn decoded_len_wrapped(&self, input: &[u8], wrap: LineWrap) -> Result<usize, DecodeError> {
69        validate_wrapped_decode::<A, PAD>(input, wrap)
70    }
71
72    /// Validates strict Base64 input without writing decoded bytes.
73    ///
74    /// This applies the same alphabet, padding, and canonical-bit checks as
75    /// [`Self::decode_slice`]. Use this method when malformed-input
76    /// diagnostics matter; use [`Self::validate`] when a boolean is enough.
77    /// This default validator is not constant-time; use
78    /// [`crate::ct::CtEngine::validate_result`] through [`Self::ct_decoder`]
79    /// for secret-bearing payloads where timing posture matters.
80    ///
81    /// # Examples
82    ///
83    /// ```
84    /// use base64_ng::STANDARD;
85    ///
86    /// STANDARD.validate_result(b"aGVsbG8=").unwrap();
87    /// assert!(STANDARD.validate_result(b"aGVsbG8").is_err());
88    /// ```
89    pub fn validate_result(&self, input: &[u8]) -> Result<(), DecodeError> {
90        validate_decode::<A, PAD>(input).map(|_| ())
91    }
92
93    /// Returns whether `input` is valid strict Base64 for this engine.
94    ///
95    /// This is a convenience wrapper around [`Self::validate_result`] and is
96    /// not constant-time. Use [`crate::ct::CtEngine::validate`] through
97    /// [`Self::ct_decoder`] for secret-bearing payloads where timing posture
98    /// matters.
99    ///
100    /// # Examples
101    ///
102    /// ```
103    /// use base64_ng::URL_SAFE_NO_PAD;
104    ///
105    /// assert!(URL_SAFE_NO_PAD.validate(b"-_8"));
106    /// assert!(!URL_SAFE_NO_PAD.validate(b"+/8"));
107    /// ```
108    #[must_use]
109    pub fn validate(&self, input: &[u8]) -> bool {
110        self.validate_result(input).is_ok()
111    }
112
113    /// Validates input using the explicit legacy whitespace profile.
114    ///
115    /// ASCII space, tab, carriage return, and line feed bytes are ignored
116    /// before applying the same alphabet, padding, and canonical-bit checks as
117    /// strict decoding.
118    ///
119    /// # Examples
120    ///
121    /// ```
122    /// use base64_ng::STANDARD;
123    ///
124    /// STANDARD.validate_legacy_result(b" aG\r\nVsbG8= ").unwrap();
125    /// assert!(STANDARD.validate_legacy_result(b" aG-=").is_err());
126    /// ```
127    pub fn validate_legacy_result(&self, input: &[u8]) -> Result<(), DecodeError> {
128        validate_legacy_decode::<A, PAD>(input).map(|_| ())
129    }
130
131    /// Returns whether `input` is valid for the explicit legacy whitespace
132    /// profile.
133    ///
134    /// This is a convenience wrapper around [`Self::validate_legacy_result`].
135    ///
136    /// # Examples
137    ///
138    /// ```
139    /// use base64_ng::STANDARD;
140    ///
141    /// assert!(STANDARD.validate_legacy(b" aG\r\nVsbG8= "));
142    /// assert!(!STANDARD.validate_legacy(b"aG-V"));
143    /// ```
144    #[must_use]
145    pub fn validate_legacy(&self, input: &[u8]) -> bool {
146        self.validate_legacy_result(input).is_ok()
147    }
148
149    /// Validates input using a strict line-wrapped profile.
150    ///
151    /// This is stricter than [`Self::validate_legacy_result`]: it accepts only
152    /// the configured line ending and enforces the configured line length for
153    /// every non-final line.
154    ///
155    /// # Examples
156    ///
157    /// ```
158    /// use base64_ng::{LineEnding, LineWrap, STANDARD};
159    ///
160    /// let wrap = LineWrap::new(4, LineEnding::Lf);
161    /// STANDARD.validate_wrapped_result(b"aGVs\nbG8=", wrap).unwrap();
162    /// assert!(STANDARD.validate_wrapped_result(b"aG\nVsbG8=", wrap).is_err());
163    /// ```
164    pub fn validate_wrapped_result(&self, input: &[u8], wrap: LineWrap) -> Result<(), DecodeError> {
165        validate_wrapped_decode::<A, PAD>(input, wrap).map(|_| ())
166    }
167
168    /// Returns whether `input` is valid for a strict line-wrapped profile.
169    ///
170    /// This is a convenience wrapper around [`Self::validate_wrapped_result`].
171    ///
172    /// # Examples
173    ///
174    /// ```
175    /// use base64_ng::{LineEnding, LineWrap, STANDARD};
176    ///
177    /// let wrap = LineWrap::new(4, LineEnding::Lf);
178    /// assert!(STANDARD.validate_wrapped(b"aGVs\nbG8=", wrap));
179    /// assert!(!STANDARD.validate_wrapped(b"aG\nVsbG8=", wrap));
180    /// ```
181    #[must_use]
182    pub fn validate_wrapped(&self, input: &[u8], wrap: LineWrap) -> bool {
183        self.validate_wrapped_result(input, wrap).is_ok()
184    }
185}