1#[cfg(feature = "alloc")]
2use crate::SecretBuffer;
3use crate::{
4 Alphabet, EncodeError, EncodedBuffer, Engine, LineWrap, checked_encoded_len,
5 encode_base64_value, scalar, wipe_bytes, wipe_tail, write_wrapped_byte, write_wrapped_bytes,
6};
7
8impl<A, const PAD: bool> Engine<A, PAD>
9where
10 A: Alphabet,
11{
12 #[must_use]
44 pub const fn encode_array<const INPUT_LEN: usize, const OUTPUT_LEN: usize>(
45 &self,
46 input: &[u8; INPUT_LEN],
47 ) -> [u8; OUTPUT_LEN] {
48 let Some(required) = checked_encoded_len(INPUT_LEN, PAD) else {
49 panic!("encoded base64 length overflows usize");
50 };
51 assert!(
52 required == OUTPUT_LEN,
53 "base64 output array has incorrect length"
54 );
55
56 let mut output = [0u8; OUTPUT_LEN];
57 let mut read = 0;
58 let mut write = 0;
59 while INPUT_LEN - read >= 3 {
60 let b0 = input[read];
61 let b1 = input[read + 1];
62 let b2 = input[read + 2];
63
64 output[write] = encode_base64_value::<A>(b0 >> 2);
65 output[write + 1] = encode_base64_value::<A>(((b0 & 0b0000_0011) << 4) | (b1 >> 4));
66 output[write + 2] = encode_base64_value::<A>(((b1 & 0b0000_1111) << 2) | (b2 >> 6));
67 output[write + 3] = encode_base64_value::<A>(b2 & 0b0011_1111);
68
69 read += 3;
70 write += 4;
71 }
72
73 match INPUT_LEN - read {
74 0 => {}
75 1 => {
76 let b0 = input[read];
77 output[write] = encode_base64_value::<A>(b0 >> 2);
78 output[write + 1] = encode_base64_value::<A>((b0 & 0b0000_0011) << 4);
79 write += 2;
80 if PAD {
81 output[write] = b'=';
82 output[write + 1] = b'=';
83 }
84 }
85 2 => {
86 let b0 = input[read];
87 let b1 = input[read + 1];
88 output[write] = encode_base64_value::<A>(b0 >> 2);
89 output[write + 1] = encode_base64_value::<A>(((b0 & 0b0000_0011) << 4) | (b1 >> 4));
90 output[write + 2] = encode_base64_value::<A>((b1 & 0b0000_1111) << 2);
91 if PAD {
92 output[write + 3] = b'=';
93 }
94 }
95 _ => unreachable!(),
96 }
97
98 output
99 }
100
101 pub fn encode_slice(&self, input: &[u8], output: &mut [u8]) -> Result<usize, EncodeError> {
103 scalar::encode_slice::<A, PAD>(input, output)
104 }
105
106 pub fn encode_slice_wrapped(
125 &self,
126 input: &[u8],
127 output: &mut [u8],
128 wrap: LineWrap,
129 ) -> Result<usize, EncodeError> {
130 let required = self.wrapped_encoded_len(input.len(), wrap)?;
131 if output.len() < required {
132 return Err(EncodeError::OutputTooSmall {
133 required,
134 available: output.len(),
135 });
136 }
137
138 let encoded_len =
139 checked_encoded_len(input.len(), PAD).ok_or(EncodeError::LengthOverflow)?;
140 if encoded_len == 0 {
141 return Ok(0);
142 }
143
144 let combined_required = match required.checked_add(encoded_len) {
147 Some(len) => len,
148 None => usize::MAX,
149 };
150 if output.len() < combined_required {
151 let mut scratch = [0u8; 1024];
152 let mut input_offset = 0;
153 let mut output_offset = 0;
154 let mut column = 0;
155
156 while input_offset < input.len() {
157 let remaining = input.len() - input_offset;
158 let mut take = remaining.min(768);
159 if remaining > take {
160 take -= take % 3;
161 }
162 if take == 0 {
163 take = remaining;
164 }
165
166 let encoded = match self
167 .encode_slice(&input[input_offset..input_offset + take], &mut scratch)
168 {
169 Ok(encoded) => encoded,
170 Err(err) => {
171 wipe_bytes(&mut scratch);
172 return Err(err);
173 }
174 };
175 if let Err(err) = write_wrapped_bytes(
176 &scratch[..encoded],
177 output,
178 &mut output_offset,
179 &mut column,
180 wrap,
181 ) {
182 wipe_bytes(&mut scratch);
183 return Err(err);
184 }
185 wipe_bytes(&mut scratch[..encoded]);
186 input_offset += take;
187 }
188
189 Ok(output_offset)
190 } else {
191 let encoded =
192 self.encode_slice(input, &mut output[required..required + encoded_len])?;
193 let mut output_offset = 0;
194 let mut column = 0;
195 let mut read = required;
196 while read < required + encoded {
197 let byte = output[read];
198 write_wrapped_byte(byte, output, &mut output_offset, &mut column, wrap)?;
199 read += 1;
200 }
201 wipe_bytes(&mut output[required..required + encoded]);
202 Ok(output_offset)
203 }
204 }
205
206 pub fn encode_slice_wrapped_clear_tail(
212 &self,
213 input: &[u8],
214 output: &mut [u8],
215 wrap: LineWrap,
216 ) -> Result<usize, EncodeError> {
217 let written = match self.encode_slice_wrapped(input, output, wrap) {
218 Ok(written) => written,
219 Err(err) => {
220 wipe_bytes(output);
221 return Err(err);
222 }
223 };
224 wipe_tail(output, written);
225 Ok(written)
226 }
227
228 pub fn encode_wrapped_buffer<const CAP: usize>(
234 &self,
235 input: &[u8],
236 wrap: LineWrap,
237 ) -> Result<EncodedBuffer<CAP>, EncodeError> {
238 let mut output = EncodedBuffer::new();
239 let written =
240 match self.encode_slice_wrapped_clear_tail(input, output.as_mut_capacity(), wrap) {
241 Ok(written) => written,
242 Err(err) => {
243 output.clear();
244 return Err(err);
245 }
246 };
247 output.set_filled(written)?;
248 Ok(output)
249 }
250
251 #[cfg(feature = "alloc")]
253 #[must_use = "for secret-bearing payloads use encode_wrapped_secret, which returns a redacted buffer with drop-time cleanup"]
254 pub fn encode_wrapped_vec(
255 &self,
256 input: &[u8],
257 wrap: LineWrap,
258 ) -> Result<alloc::vec::Vec<u8>, EncodeError> {
259 let required = self.wrapped_encoded_len(input.len(), wrap)?;
260 let mut output = alloc::vec![0; required];
261 let written = self.encode_slice_wrapped(input, &mut output, wrap)?;
262 output.truncate(written);
263 Ok(output)
264 }
265
266 #[cfg(feature = "alloc")]
268 pub fn encode_wrapped_string(
269 &self,
270 input: &[u8],
271 wrap: LineWrap,
272 ) -> Result<alloc::string::String, EncodeError> {
273 let output = self.encode_wrapped_vec(input, wrap)?;
274 match alloc::string::String::from_utf8(output) {
275 Ok(output) => Ok(output),
276 Err(_) => unreachable!("base64 encoder produced non-UTF-8 output"),
277 }
278 }
279
280 #[cfg(feature = "alloc")]
285 pub fn encode_wrapped_secret(
286 &self,
287 input: &[u8],
288 wrap: LineWrap,
289 ) -> Result<SecretBuffer, EncodeError> {
290 self.encode_wrapped_vec(input, wrap)
291 .map(SecretBuffer::from_vec)
292 }
293
294 pub fn encode_slice_clear_tail(
314 &self,
315 input: &[u8],
316 output: &mut [u8],
317 ) -> Result<usize, EncodeError> {
318 let written = match self.encode_slice(input, output) {
319 Ok(written) => written,
320 Err(err) => {
321 wipe_bytes(output);
322 return Err(err);
323 }
324 };
325 wipe_tail(output, written);
326 Ok(written)
327 }
328
329 pub fn encode_buffer<const CAP: usize>(
344 &self,
345 input: &[u8],
346 ) -> Result<EncodedBuffer<CAP>, EncodeError> {
347 let mut output = EncodedBuffer::new();
348 let written = match self.encode_slice_clear_tail(input, output.as_mut_capacity()) {
349 Ok(written) => written,
350 Err(err) => {
351 output.clear();
352 return Err(err);
353 }
354 };
355 output.set_filled(written)?;
356 Ok(output)
357 }
358
359 #[cfg(feature = "alloc")]
361 #[must_use = "for secret-bearing payloads use encode_secret, which returns a redacted buffer with drop-time cleanup"]
362 pub fn encode_vec(&self, input: &[u8]) -> Result<alloc::vec::Vec<u8>, EncodeError> {
363 let required = checked_encoded_len(input.len(), PAD).ok_or(EncodeError::LengthOverflow)?;
364 let mut output = alloc::vec![0; required];
365 let written = self.encode_slice(input, &mut output)?;
366 output.truncate(written);
367 Ok(output)
368 }
369
370 #[cfg(feature = "alloc")]
375 pub fn encode_secret(&self, input: &[u8]) -> Result<SecretBuffer, EncodeError> {
376 self.encode_vec(input).map(SecretBuffer::from_vec)
377 }
378
379 #[cfg(feature = "alloc")]
394 pub fn encode_string(&self, input: &[u8]) -> Result<alloc::string::String, EncodeError> {
395 let output = self.encode_vec(input)?;
396 match alloc::string::String::from_utf8(output) {
397 Ok(output) => Ok(output),
398 Err(_) => unreachable!("base64 encoder produced non-UTF-8 output"),
399 }
400 }
401}