192f3ab15Sopenharmony_ci//! Base64 encoding support.
292f3ab15Sopenharmony_ciuse crate::error::ErrorStack;
392f3ab15Sopenharmony_ciuse crate::{cvt_n, LenType};
492f3ab15Sopenharmony_ciuse libc::c_int;
592f3ab15Sopenharmony_ciuse openssl_macros::corresponds;
692f3ab15Sopenharmony_ci
792f3ab15Sopenharmony_ci/// Encodes a slice of bytes to a base64 string.
892f3ab15Sopenharmony_ci///
992f3ab15Sopenharmony_ci/// # Panics
1092f3ab15Sopenharmony_ci///
1192f3ab15Sopenharmony_ci/// Panics if the input length or computed output length overflow a signed C integer.
1292f3ab15Sopenharmony_ci#[corresponds(EVP_EncodeBlock)]
1392f3ab15Sopenharmony_cipub fn encode_block(src: &[u8]) -> String {
1492f3ab15Sopenharmony_ci    assert!(src.len() <= c_int::max_value() as usize);
1592f3ab15Sopenharmony_ci    let src_len = src.len() as LenType;
1692f3ab15Sopenharmony_ci
1792f3ab15Sopenharmony_ci    let len = encoded_len(src_len).unwrap();
1892f3ab15Sopenharmony_ci    let mut out = Vec::with_capacity(len as usize);
1992f3ab15Sopenharmony_ci
2092f3ab15Sopenharmony_ci    // SAFETY: `encoded_len` ensures space for 4 output characters
2192f3ab15Sopenharmony_ci    // for every 3 input bytes including padding and nul terminator.
2292f3ab15Sopenharmony_ci    // `EVP_EncodeBlock` will write only single byte ASCII characters.
2392f3ab15Sopenharmony_ci    // `EVP_EncodeBlock` will only write to not read from `out`.
2492f3ab15Sopenharmony_ci    unsafe {
2592f3ab15Sopenharmony_ci        let out_len = ffi::EVP_EncodeBlock(out.as_mut_ptr(), src.as_ptr(), src_len);
2692f3ab15Sopenharmony_ci        out.set_len(out_len as usize);
2792f3ab15Sopenharmony_ci        String::from_utf8_unchecked(out)
2892f3ab15Sopenharmony_ci    }
2992f3ab15Sopenharmony_ci}
3092f3ab15Sopenharmony_ci
3192f3ab15Sopenharmony_ci/// Decodes a base64-encoded string to bytes.
3292f3ab15Sopenharmony_ci///
3392f3ab15Sopenharmony_ci/// # Panics
3492f3ab15Sopenharmony_ci///
3592f3ab15Sopenharmony_ci/// Panics if the input length or computed output length overflow a signed C integer.
3692f3ab15Sopenharmony_ci#[corresponds(EVP_DecodeBlock)]
3792f3ab15Sopenharmony_cipub fn decode_block(src: &str) -> Result<Vec<u8>, ErrorStack> {
3892f3ab15Sopenharmony_ci    let src = src.trim();
3992f3ab15Sopenharmony_ci
4092f3ab15Sopenharmony_ci    // https://github.com/openssl/openssl/issues/12143
4192f3ab15Sopenharmony_ci    if src.is_empty() {
4292f3ab15Sopenharmony_ci        return Ok(vec![]);
4392f3ab15Sopenharmony_ci    }
4492f3ab15Sopenharmony_ci
4592f3ab15Sopenharmony_ci    assert!(src.len() <= c_int::max_value() as usize);
4692f3ab15Sopenharmony_ci    let src_len = src.len() as LenType;
4792f3ab15Sopenharmony_ci
4892f3ab15Sopenharmony_ci    let len = decoded_len(src_len).unwrap();
4992f3ab15Sopenharmony_ci    let mut out = Vec::with_capacity(len as usize);
5092f3ab15Sopenharmony_ci
5192f3ab15Sopenharmony_ci    // SAFETY: `decoded_len` ensures space for 3 output bytes
5292f3ab15Sopenharmony_ci    // for every 4 input characters including padding.
5392f3ab15Sopenharmony_ci    // `EVP_DecodeBlock` can write fewer bytes after stripping
5492f3ab15Sopenharmony_ci    // leading and trailing whitespace, but never more.
5592f3ab15Sopenharmony_ci    // `EVP_DecodeBlock` will only write to not read from `out`.
5692f3ab15Sopenharmony_ci    unsafe {
5792f3ab15Sopenharmony_ci        let out_len = cvt_n(ffi::EVP_DecodeBlock(
5892f3ab15Sopenharmony_ci            out.as_mut_ptr(),
5992f3ab15Sopenharmony_ci            src.as_ptr(),
6092f3ab15Sopenharmony_ci            src_len,
6192f3ab15Sopenharmony_ci        ))?;
6292f3ab15Sopenharmony_ci        out.set_len(out_len as usize);
6392f3ab15Sopenharmony_ci    }
6492f3ab15Sopenharmony_ci
6592f3ab15Sopenharmony_ci    if src.ends_with('=') {
6692f3ab15Sopenharmony_ci        out.pop();
6792f3ab15Sopenharmony_ci        if src.ends_with("==") {
6892f3ab15Sopenharmony_ci            out.pop();
6992f3ab15Sopenharmony_ci        }
7092f3ab15Sopenharmony_ci    }
7192f3ab15Sopenharmony_ci
7292f3ab15Sopenharmony_ci    Ok(out)
7392f3ab15Sopenharmony_ci}
7492f3ab15Sopenharmony_ci
7592f3ab15Sopenharmony_cifn encoded_len(src_len: LenType) -> Option<LenType> {
7692f3ab15Sopenharmony_ci    let mut len = (src_len / 3).checked_mul(4)?;
7792f3ab15Sopenharmony_ci
7892f3ab15Sopenharmony_ci    if src_len % 3 != 0 {
7992f3ab15Sopenharmony_ci        len = len.checked_add(4)?;
8092f3ab15Sopenharmony_ci    }
8192f3ab15Sopenharmony_ci
8292f3ab15Sopenharmony_ci    len = len.checked_add(1)?;
8392f3ab15Sopenharmony_ci
8492f3ab15Sopenharmony_ci    Some(len)
8592f3ab15Sopenharmony_ci}
8692f3ab15Sopenharmony_ci
8792f3ab15Sopenharmony_cifn decoded_len(src_len: LenType) -> Option<LenType> {
8892f3ab15Sopenharmony_ci    let mut len = (src_len / 4).checked_mul(3)?;
8992f3ab15Sopenharmony_ci
9092f3ab15Sopenharmony_ci    if src_len % 4 != 0 {
9192f3ab15Sopenharmony_ci        len = len.checked_add(3)?;
9292f3ab15Sopenharmony_ci    }
9392f3ab15Sopenharmony_ci
9492f3ab15Sopenharmony_ci    Some(len)
9592f3ab15Sopenharmony_ci}
9692f3ab15Sopenharmony_ci
9792f3ab15Sopenharmony_ci#[cfg(test)]
9892f3ab15Sopenharmony_cimod tests {
9992f3ab15Sopenharmony_ci    use super::*;
10092f3ab15Sopenharmony_ci
10192f3ab15Sopenharmony_ci    #[test]
10292f3ab15Sopenharmony_ci    fn test_encode_block() {
10392f3ab15Sopenharmony_ci        assert_eq!("".to_string(), encode_block(b""));
10492f3ab15Sopenharmony_ci        assert_eq!("Zg==".to_string(), encode_block(b"f"));
10592f3ab15Sopenharmony_ci        assert_eq!("Zm8=".to_string(), encode_block(b"fo"));
10692f3ab15Sopenharmony_ci        assert_eq!("Zm9v".to_string(), encode_block(b"foo"));
10792f3ab15Sopenharmony_ci        assert_eq!("Zm9vYg==".to_string(), encode_block(b"foob"));
10892f3ab15Sopenharmony_ci        assert_eq!("Zm9vYmE=".to_string(), encode_block(b"fooba"));
10992f3ab15Sopenharmony_ci        assert_eq!("Zm9vYmFy".to_string(), encode_block(b"foobar"));
11092f3ab15Sopenharmony_ci    }
11192f3ab15Sopenharmony_ci
11292f3ab15Sopenharmony_ci    #[test]
11392f3ab15Sopenharmony_ci    fn test_decode_block() {
11492f3ab15Sopenharmony_ci        assert_eq!(b"".to_vec(), decode_block("").unwrap());
11592f3ab15Sopenharmony_ci        assert_eq!(b"f".to_vec(), decode_block("Zg==").unwrap());
11692f3ab15Sopenharmony_ci        assert_eq!(b"fo".to_vec(), decode_block("Zm8=").unwrap());
11792f3ab15Sopenharmony_ci        assert_eq!(b"foo".to_vec(), decode_block("Zm9v").unwrap());
11892f3ab15Sopenharmony_ci        assert_eq!(b"foob".to_vec(), decode_block("Zm9vYg==").unwrap());
11992f3ab15Sopenharmony_ci        assert_eq!(b"fooba".to_vec(), decode_block("Zm9vYmE=").unwrap());
12092f3ab15Sopenharmony_ci        assert_eq!(b"foobar".to_vec(), decode_block("Zm9vYmFy").unwrap());
12192f3ab15Sopenharmony_ci    }
12292f3ab15Sopenharmony_ci
12392f3ab15Sopenharmony_ci    #[test]
12492f3ab15Sopenharmony_ci    fn test_strip_whitespace() {
12592f3ab15Sopenharmony_ci        assert_eq!(b"foobar".to_vec(), decode_block(" Zm9vYmFy\n").unwrap());
12692f3ab15Sopenharmony_ci        assert_eq!(b"foob".to_vec(), decode_block(" Zm9vYg==\n").unwrap());
12792f3ab15Sopenharmony_ci    }
12892f3ab15Sopenharmony_ci}
129