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