use crate::utils::base64::{decode_rfc2045, decode_rfc4648, get_decoded_buffer_size, Decoder};
use base64::{
engine::general_purpose::{STANDARD, STANDARD_NO_PAD},
Engine,
};
use libc::c_ulong;
use std::os::raw::c_uchar;
#[repr(C)]
#[allow(non_camel_case_types)]
pub enum SCBase64ReturnCode {
SC_BASE64_OK = 0,
SC_BASE64_INVALID_ARG,
SC_BASE64_OVERFLOW,
}
#[repr(u8)]
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum SCBase64Mode {
SCBase64ModeRFC2045 = 0,
SCBase64ModeStrict,
SCBase64ModeRFC4648,
SCBase64ModeNoPad,
SCBase64ModePadOpt,
}
#[no_mangle]
pub unsafe extern "C" fn SCBase64DecodeBufferSize(input_len: u32) -> u32 {
return get_decoded_buffer_size(input_len);
}
#[no_mangle]
pub unsafe extern "C" fn SCBase64Decode(
input: *const u8, len: usize, mode: SCBase64Mode, output: *mut u8,
) -> u32 {
if input.is_null() || len == 0 {
return 0;
}
let in_vec = build_slice!(input, len);
let out_vec = std::slice::from_raw_parts_mut(output, len);
let mut num_decoded: u32 = 0;
let mut decoder = Decoder::new();
match mode {
SCBase64Mode::SCBase64ModeRFC2045 => {
if decode_rfc2045(&mut decoder, in_vec, out_vec, &mut num_decoded).is_err() {
debug_validate_bug_on!(num_decoded >= len as u32);
return num_decoded;
}
}
SCBase64Mode::SCBase64ModeRFC4648 => {
if decode_rfc4648(&mut decoder, in_vec, out_vec, &mut num_decoded).is_err() {
debug_validate_bug_on!(num_decoded >= len as u32);
return num_decoded;
}
}
SCBase64Mode::SCBase64ModeStrict => {
if let Ok(decoded_len) = STANDARD.decode_slice(in_vec, out_vec) {
num_decoded = decoded_len as u32;
}
}
SCBase64Mode::SCBase64ModeNoPad => {
if let Ok(decoded_len) = STANDARD_NO_PAD.decode_slice(in_vec, out_vec) {
num_decoded = decoded_len as u32;
}
}
SCBase64Mode::SCBase64ModePadOpt => {
let config = base64::engine::GeneralPurposeConfig::new()
.with_decode_padding_mode(base64::engine::DecodePaddingMode::Indifferent);
let decoder = base64::engine::GeneralPurpose::new(&base64::alphabet::STANDARD, config);
if let Ok(decoded_len) = decoder.decode_slice(in_vec, out_vec) {
num_decoded = decoded_len as u32;
}
}
}
debug_validate_bug_on!(num_decoded >= len as u32);
return num_decoded;
}
#[no_mangle]
pub unsafe extern "C" fn SCBase64EncodeWithMode(
input: *const u8, input_len: c_ulong, output: *mut c_uchar, output_len: *mut c_ulong,
mode: SCBase64Mode,
) -> SCBase64ReturnCode {
if input.is_null() || output.is_null() || output_len.is_null() {
return SCBase64ReturnCode::SC_BASE64_INVALID_ARG;
}
let input = std::slice::from_raw_parts(input, input_len as usize);
let encoded = match mode {
SCBase64Mode::SCBase64ModeNoPad => STANDARD_NO_PAD.encode(input),
_ => STANDARD.encode(input),
};
if encoded.len() + 1 > *output_len as usize {
return SCBase64ReturnCode::SC_BASE64_OVERFLOW;
}
let output = std::slice::from_raw_parts_mut(&mut *output, *output_len as usize);
output[0..encoded.len()].copy_from_slice(encoded.as_bytes());
output[encoded.len()] = 0;
*output_len = encoded.len() as c_ulong;
SCBase64ReturnCode::SC_BASE64_OK
}
#[no_mangle]
pub unsafe extern "C" fn SCBase64Encode(
input: *const u8, input_len: c_ulong, output: *mut c_uchar, output_len: *mut c_ulong,
) -> SCBase64ReturnCode {
SCBase64EncodeWithMode(
input,
input_len,
output,
output_len,
SCBase64Mode::SCBase64ModeStrict,
)
}
#[no_mangle]
pub extern "C" fn SCBase64EncodeBufferSize(len: c_ulong) -> c_ulong {
(4 * ((len) + 2) / 3) + 1
}