Skip to main content

codec_base64url/
decode.rs

1// SPDX-FileCopyrightText: 2026 ReallyMe LLC
2//
3// SPDX-License-Identifier: MIT OR Apache-2.0
4
5use base64::{decoded_len_estimate, engine::general_purpose::URL_SAFE_NO_PAD, Engine as _};
6use zeroize::Zeroizing;
7
8use crate::{Base64UrlError, MAX_BASE64URL_INPUT_LEN};
9
10/// Decode RFC 4648 section 5 Base64URL without padding.
11pub fn base64url_to_bytes(s: &str) -> Result<Vec<u8>, Base64UrlError> {
12    base64url_bytes_to_bytes(s.as_bytes())
13}
14
15/// Decode RFC 4648 section 5 Base64URL bytes without padding.
16///
17/// This is useful for compact JWT/JWS/JWE segments that have already been
18/// split as bytes. It avoids requiring callers to first prove UTF-8 just to
19/// decode an ASCII Base64URL alphabet.
20pub fn base64url_bytes_to_bytes(bytes: &[u8]) -> Result<Vec<u8>, Base64UrlError> {
21    if bytes.len() > MAX_BASE64URL_INPUT_LEN {
22        return Err(Base64UrlError::InputTooLarge);
23    }
24    // Retain the partially decoded allocation until validation completes so
25    // malformed input cannot leave decoded material in a freed heap block.
26    let mut output = Zeroizing::new(vec![0_u8; decoded_len_estimate(bytes.len())]);
27    let length = URL_SAFE_NO_PAD
28        .decode_slice(bytes, output.as_mut_slice())
29        .map_err(|_| Base64UrlError::Invalid)?;
30    output.truncate(length);
31    Ok(core::mem::take(&mut *output))
32}