rust/base64: add decoder

Add a pure rust base64 decoder. This supports 3 modes of operation just
like the C decoder as follows.
1. RFC 2045
2. RFC 4648
3. Strict

One notable change is that "strict" mode is carried out by the rust
base64 crate instead of native Rust. This crate was already used for
encoding in a few places like datasets of string type. As a part of this
mode, now, only the strings that can be reliably converted back are
decoded.

The decoder fn is available to C via FFI.

Bug 6280
Ticket 7065
Ticket 7058
pull/11823/head
Shivani Bhardwaj 8 months ago committed by Victor Julien
parent 7ab833471e
commit cbb571a61c

@ -21,6 +21,7 @@ use crate::detect::error::RuleParseError;
use crate::detect::parser::{parse_var, take_until_whitespace, ResultValue};
use std::ffi::{CStr, CString};
use std::os::raw::c_char;
use crate::ffi::base64::Base64Mode;
use nom7::bytes::complete::tag;
use nom7::character::complete::multispace0;
@ -28,46 +29,7 @@ use nom7::sequence::preceded;
use nom7::{Err, IResult};
use std::str;
#[repr(u8)]
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum DetectBase64Mode {
Base64ModeRelax = 0,
/* If the following strings were to be passed to the decoder with RFC2045 mode,
* the results would be as follows. See the unittest B64TestVectorsRFC2045 in
* src/util-base64.c
*
* BASE64("") = ""
* BASE64("f") = "Zg=="
* BASE64("fo") = "Zm8="
* BASE64("foo") = "Zm9v"
* BASE64("foob") = "Zm9vYg=="
* BASE64("fooba") = "Zm9vYmE="
* BASE64("foobar") = "Zm9vYmFy"
* BASE64("foobar") = "Zm 9v Ym Fy" <-- Notice how the spaces are ignored
* BASE64("foobar") = "Zm$9vYm.Fy" # According to RFC 2045, All line breaks or *other
* characters* not found in base64 alphabet must be ignored by decoding software
* */
Base64ModeRFC2045, /* SPs are allowed during transfer but must be skipped by Decoder */
Base64ModeStrict,
/* If the following strings were to be passed to the decoder with RFC4648 mode,
* the results would be as follows. See the unittest B64TestVectorsRFC4648 in
* src/util-base64.c
*
* BASE64("") = ""
* BASE64("f") = "Zg=="
* BASE64("fo") = "Zm8="
* BASE64("foo") = "Zm9v"
* BASE64("foob") = "Zm9vYg=="
* BASE64("fooba") = "Zm9vYmE="
* BASE64("foobar") = "Zm9vYmFy"
* BASE64("f") = "Zm 9v Ym Fy" <-- Notice how the processing stops once space is encountered
* BASE64("f") = "Zm$9vYm.Fy" <-- Notice how the processing stops once an invalid char is
* encountered
* */
Base64ModeRFC4648, /* reject the encoded data if it contains characters outside the base alphabet */
}
pub const TRANSFORM_FROM_BASE64_MODE_DEFAULT: DetectBase64Mode = DetectBase64Mode::Base64ModeRFC4648;
pub const TRANSFORM_FROM_BASE64_MODE_DEFAULT: Base64Mode = Base64Mode::Base64ModeRFC4648;
const DETECT_TRANSFORM_BASE64_MAX_PARAM_COUNT: usize = 3;
pub const DETECT_TRANSFORM_BASE64_FLAG_MODE: u8 = 0x01;
@ -84,7 +46,7 @@ pub struct SCDetectTransformFromBase64Data {
nbytes_str: *const c_char,
offset: u32,
offset_str: *const c_char,
mode: DetectBase64Mode,
mode: Base64Mode,
}
impl Drop for SCDetectTransformFromBase64Data {
@ -120,11 +82,11 @@ impl SCDetectTransformFromBase64Data {
}
}
fn get_mode_value(value: &str) -> Option<DetectBase64Mode> {
fn get_mode_value(value: &str) -> Option<Base64Mode> {
let res = match value {
"rfc4648" => Some(DetectBase64Mode::Base64ModeRFC4648),
"rfc2045" => Some(DetectBase64Mode::Base64ModeRFC2045),
"strict" => Some(DetectBase64Mode::Base64ModeStrict),
"rfc4648" => Some(Base64Mode::Base64ModeRFC4648),
"rfc2045" => Some(Base64Mode::Base64ModeRFC2045),
"strict" => Some(Base64Mode::Base64ModeStrict),
_ => None,
};
@ -307,7 +269,7 @@ mod tests {
nbytes_str: &str,
offset: u32,
offset_str: &str,
mode: DetectBase64Mode,
mode: Base64Mode,
flags: u8,
) {
let tbd = SCDetectTransformFromBase64Data {
@ -365,7 +327,7 @@ mod tests {
assert_eq!(val, tbd);
tbd.flags = DETECT_TRANSFORM_BASE64_FLAG_MODE;
tbd.mode = DetectBase64Mode::Base64ModeRFC2045;
tbd.mode = Base64Mode::Base64ModeRFC2045;
tbd.offset = 0;
tbd.nbytes = 0;
let (_, val) = parse_transform_base64("mode rfc2045").unwrap();
@ -382,7 +344,7 @@ mod tests {
"",
3933,
"",
DetectBase64Mode::Base64ModeStrict,
Base64Mode::Base64ModeStrict,
DETECT_TRANSFORM_BASE64_FLAG_NBYTES
| DETECT_TRANSFORM_BASE64_FLAG_OFFSET
| DETECT_TRANSFORM_BASE64_FLAG_MODE,
@ -394,7 +356,7 @@ mod tests {
"",
3933,
"",
DetectBase64Mode::Base64ModeRFC2045,
Base64Mode::Base64ModeRFC2045,
DETECT_TRANSFORM_BASE64_FLAG_NBYTES
| DETECT_TRANSFORM_BASE64_FLAG_OFFSET
| DETECT_TRANSFORM_BASE64_FLAG_MODE,
@ -406,7 +368,7 @@ mod tests {
"",
3933,
"",
DetectBase64Mode::Base64ModeRFC4648,
Base64Mode::Base64ModeRFC4648,
DETECT_TRANSFORM_BASE64_FLAG_NBYTES
| DETECT_TRANSFORM_BASE64_FLAG_OFFSET
| DETECT_TRANSFORM_BASE64_FLAG_MODE,
@ -418,7 +380,7 @@ mod tests {
"",
0,
"var",
DetectBase64Mode::Base64ModeRFC4648,
Base64Mode::Base64ModeRFC4648,
DETECT_TRANSFORM_BASE64_FLAG_NBYTES
| DETECT_TRANSFORM_BASE64_FLAG_OFFSET_VAR
| DETECT_TRANSFORM_BASE64_FLAG_OFFSET
@ -431,7 +393,7 @@ mod tests {
"var",
3933,
"",
DetectBase64Mode::Base64ModeRFC4648,
Base64Mode::Base64ModeRFC4648,
DETECT_TRANSFORM_BASE64_FLAG_NBYTES
| DETECT_TRANSFORM_BASE64_FLAG_NBYTES_VAR
| DETECT_TRANSFORM_BASE64_FLAG_OFFSET

@ -15,8 +15,9 @@
* 02110-1301, USA.
*/
use std::os::raw::c_uchar;
use crate::utils::base64::{decode_rfc4648, decode_rfc2045, get_decoded_buffer_size, Decoder};
use libc::c_ulong;
use std::os::raw::c_uchar;
use base64::{Engine, engine::general_purpose::STANDARD};
#[repr(C)]
@ -27,6 +28,90 @@ pub enum Base64ReturnCode {
SC_BASE64_OVERFLOW,
}
#[repr(C)]
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum Base64Mode {
/* If the following strings were to be passed to the decoder with RFC2045 mode,
* the results would be as follows. See the unittest B64TestVectorsRFC2045 in
* src/util-base64.c
*
* BASE64("") = ""
* BASE64("f") = "Zg=="
* BASE64("fo") = "Zm8="
* BASE64("foo") = "Zm9v"
* BASE64("foob") = "Zm9vYg=="
* BASE64("fooba") = "Zm9vYmE="
* BASE64("foobar") = "Zm9vYmFy"
* BASE64("foobar") = "Zm 9v Ym Fy" <-- Notice how the spaces are ignored
* BASE64("foobar") = "Zm$9vYm.Fy" # According to RFC 2045, All line breaks or *other
* characters* not found in base64 alphabet must be ignored by decoding software
* */
Base64ModeRFC2045 = 0, /* SPs are allowed during transfer but must be skipped by Decoder */
Base64ModeStrict,
/* If the following strings were to be passed to the decoder with RFC4648 mode,
* the results would be as follows. See the unittest B64TestVectorsRFC4648 in
* src/util-base64.c
*
* BASE64("") = ""
* BASE64("f") = "Zg=="
* BASE64("fo") = "Zm8="
* BASE64("foo") = "Zm9v"
* BASE64("foob") = "Zm9vYg=="
* BASE64("fooba") = "Zm9vYmE="
* BASE64("foobar") = "Zm9vYmFy"
* BASE64("f") = "Zm 9v Ym Fy" <-- Notice how the processing stops once space is encountered
* BASE64("f") = "Zm$9vYm.Fy" <-- Notice how the processing stops once an invalid char is
* encountered
* */
Base64ModeRFC4648, /* reject the encoded data if it contains characters outside the base alphabet */
}
#[no_mangle]
pub unsafe extern "C" fn Base64DecodeBufferSize(input_len: u32) -> u32 {
return get_decoded_buffer_size(input_len);
}
/// Base64 decode a buffer.
///
/// This method exposes the Rust base64 decoder to C and should not be called from
/// Rust code.
///
/// It allows decoding in the modes described by ``Base64Mode`` enum.
#[no_mangle]
pub unsafe extern "C" fn Base64Decode(
input: *const u8, len: usize, mode: Base64Mode, 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 {
Base64Mode::Base64ModeRFC2045 => {
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;
}
}
Base64Mode::Base64ModeRFC4648 => {
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;
}
}
Base64Mode::Base64ModeStrict => {
if let Ok(decoded_len) = STANDARD.decode_slice(in_vec, out_vec) {
num_decoded = decoded_len as u32;
}
}
}
debug_validate_bug_on!(num_decoded >= len as u32);
return num_decoded;
}
/// Base64 encode a buffer.
///
/// This method exposes the Rust base64 encoder to C and should not be called from

@ -88,6 +88,7 @@ pub mod filecontainer;
pub mod filetracker;
pub mod kerberos;
pub mod detect;
pub mod utils;
pub mod ja4;

@ -16,6 +16,7 @@
*/
use super::mime;
use crate::utils::base64;
use crate::core::StreamingBufferConfig;
use crate::filecontainer::FileContainer;
use digest::generic_array::{typenum::U16, GenericArray};
@ -23,7 +24,6 @@ use digest::Digest;
use digest::Update;
use md5::Md5;
use std::ffi::CStr;
use std::io;
use std::os::raw::c_uchar;
#[repr(u8)]
@ -81,7 +81,7 @@ pub struct MimeStateSMTP<'a> {
pub(crate) urls: Vec<Vec<u8>>,
boundaries: Vec<Vec<u8>>,
encoding: MimeSmtpEncoding,
decoder: Option<MimeBase64Decoder>,
decoder: Option<base64::Decoder>,
content_type: MimeSmtpContentType,
decoded_line: Vec<u8>,
// small buffer for end of line
@ -95,24 +95,6 @@ pub struct MimeStateSMTP<'a> {
pub(crate) md5_result: GenericArray<u8, U16>,
}
#[derive(Debug)]
pub struct MimeBase64Decoder {
tmp: [u8; 4],
nb: u8,
}
impl MimeBase64Decoder {
pub fn new() -> MimeBase64Decoder {
MimeBase64Decoder { tmp: [0; 4], nb: 0 }
}
}
impl Default for MimeBase64Decoder {
fn default() -> Self {
Self::new()
}
}
pub fn mime_smtp_state_init(
files: &mut FileContainer, sbcfg: *const StreamingBufferConfig,
) -> Option<MimeStateSMTP> {
@ -216,7 +198,7 @@ fn mime_smtp_process_headers(ctx: &mut MimeStateSMTP) -> (u32, bool) {
} else if mime::slice_equals_lowercase(&h.name, b"content-transfer-encoding") {
if mime::slice_equals_lowercase(&h.value, b"base64") {
ctx.encoding = MimeSmtpEncoding::Base64;
ctx.decoder = Some(MimeBase64Decoder::new());
ctx.decoder = Some(base64::Decoder::new());
} else if mime::slice_equals_lowercase(&h.value, b"quoted-printable") {
ctx.encoding = MimeSmtpEncoding::QuotedPrintable;
}
@ -378,116 +360,6 @@ fn mime_smtp_find_url_strings(ctx: &mut MimeStateSMTP, input_new: &[u8]) {
}
}
fn mime_base64_map(input: u8) -> io::Result<u8> {
match input {
43 => Ok(62), // +
47 => Ok(63), // /
48 => Ok(52), // 0
49 => Ok(53), // 1
50 => Ok(54), // 2
51 => Ok(55), // 3
52 => Ok(56), // 4
53 => Ok(57), // 5
54 => Ok(58), // 6
55 => Ok(59), // 7
56 => Ok(60), // 8
57 => Ok(61), // 9
65 => Ok(0), // A
66 => Ok(1), // B
67 => Ok(2), // C
68 => Ok(3), // D
69 => Ok(4), // E
70 => Ok(5), // F
71 => Ok(6), // G
72 => Ok(7), // H
73 => Ok(8), // I
74 => Ok(9), // J
75 => Ok(10), // K
76 => Ok(11), // L
77 => Ok(12), // M
78 => Ok(13), // N
79 => Ok(14), // O
80 => Ok(15), // P
81 => Ok(16), // Q
82 => Ok(17), // R
83 => Ok(18), // S
84 => Ok(19), // T
85 => Ok(20), // U
86 => Ok(21), // V
87 => Ok(22), // W
88 => Ok(23), // X
89 => Ok(24), // Y
90 => Ok(25), // Z
97 => Ok(26), // a
98 => Ok(27), // b
99 => Ok(28), // c
100 => Ok(29), // d
101 => Ok(30), // e
102 => Ok(31), // f
103 => Ok(32), // g
104 => Ok(33), // h
105 => Ok(34), // i
106 => Ok(35), // j
107 => Ok(36), // k
108 => Ok(37), // l
109 => Ok(38), // m
110 => Ok(39), // n
111 => Ok(40), // o
112 => Ok(41), // p
113 => Ok(42), // q
114 => Ok(43), // r
115 => Ok(44), // s
116 => Ok(45), // t
117 => Ok(46), // u
118 => Ok(47), // v
119 => Ok(48), // w
120 => Ok(49), // x
121 => Ok(50), // y
122 => Ok(51), // z
_ => Err(io::Error::new(io::ErrorKind::InvalidData, "invalid base64")),
}
}
fn mime_base64_decode(decoder: &mut MimeBase64Decoder, input: &[u8]) -> io::Result<Vec<u8>> {
let mut i = input;
let maxlen = ((decoder.nb as usize + i.len()) * 3) / 4;
let mut r = vec![0; maxlen];
let mut offset = 0;
while !i.is_empty() {
while decoder.nb < 4 && !i.is_empty() {
if mime_base64_map(i[0]).is_ok() || i[0] == b'=' {
decoder.tmp[decoder.nb as usize] = i[0];
decoder.nb += 1;
}
i = &i[1..];
}
if decoder.nb == 4 {
decoder.tmp[0] = mime_base64_map(decoder.tmp[0])?;
decoder.tmp[1] = mime_base64_map(decoder.tmp[1])?;
if decoder.tmp[2] == b'=' {
r[offset] = (decoder.tmp[0] << 2) | (decoder.tmp[1] >> 4);
offset += 1;
} else {
decoder.tmp[2] = mime_base64_map(decoder.tmp[2])?;
if decoder.tmp[3] == b'=' {
r[offset] = (decoder.tmp[0] << 2) | (decoder.tmp[1] >> 4);
r[offset + 1] = (decoder.tmp[1] << 4) | (decoder.tmp[2] >> 2);
offset += 2;
} else {
decoder.tmp[3] = mime_base64_map(decoder.tmp[3])?;
r[offset] = (decoder.tmp[0] << 2) | (decoder.tmp[1] >> 4);
r[offset + 1] = (decoder.tmp[1] << 4) | (decoder.tmp[2] >> 2);
r[offset + 2] = (decoder.tmp[2] << 6) | decoder.tmp[3];
offset += 3;
}
}
decoder.nb = 0;
}
}
r.truncate(offset);
return Ok(r);
}
const MAX_LINE_LEN: u32 = 998; // Def in RFC 2045, excluding CRLF sequence
const MAX_ENC_LINE_LEN: usize = 76; /* Def in RFC 2045, excluding CRLF sequence */
const MAX_HEADER_NAME: usize = 75; /* 75 + ":" = 76 */
@ -584,13 +456,16 @@ fn mime_smtp_parse_line(
for _i in 0..4 - decoder.nb {
v.push(b'=');
}
if let Ok(dec) = mime_base64_decode(decoder, &v) {
let dec_size = base64::get_decoded_buffer_size((decoder.nb as usize + v.len()) as u32);
let mut dec = vec![0; dec_size as usize];
let mut dec_len = 0;
if base64::decode_rfc2045(decoder, &v, &mut dec, &mut dec_len).is_ok() {
unsafe {
FileAppendData(
ctx.files,
ctx.sbcfg,
dec.as_ptr(),
dec.len() as u32,
dec_len,
);
}
}
@ -629,7 +504,10 @@ fn mime_smtp_parse_line(
if i.len() > MAX_ENC_LINE_LEN {
warnings |= MIME_ANOM_LONG_ENC_LINE;
}
if let Ok(dec) = mime_base64_decode(decoder, i) {
let dec_size = base64::get_decoded_buffer_size((decoder.nb as usize + i.len()) as u32);
let mut dec = vec![0; dec_size as usize];
let mut dec_len = 0; // unnecessary but required by fn args
if base64::decode_rfc2045(decoder, i, &mut dec, &mut dec_len).is_ok() {
mime_smtp_find_url_strings(ctx, &dec);
} else {
warnings |= MIME_ANOM_INVALID_BASE64;
@ -668,14 +546,17 @@ fn mime_smtp_parse_line(
if i.len() > MAX_ENC_LINE_LEN {
warnings |= MIME_ANOM_LONG_ENC_LINE;
}
if let Ok(dec) = mime_base64_decode(decoder, i) {
let dec_size = base64::get_decoded_buffer_size((decoder.nb as usize + i.len()) as u32);
let mut dec = vec![0; dec_size as usize];
let mut dec_len = 0;
if base64::decode_rfc2045(decoder, i, &mut dec, &mut dec_len).is_ok() {
mime_smtp_find_url_strings(ctx, &dec);
unsafe {
FileAppendData(
ctx.files,
ctx.sbcfg,
dec.as_ptr(),
dec.len() as u32,
dec_len,
);
}
} else {

@ -0,0 +1,207 @@
/* Copyright (C) 2024 Open Information Security Foundation
*
* You can copy, redistribute or modify this Program under the terms of
* the GNU General Public License version 2 as published by the Free
* Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* version 2 along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
// Author: Shivani Bhardwaj <shivani@oisf.net>
use std::io::{Error, ErrorKind, Result};
fn base64_map(input: u8) -> Result<u8> {
match input {
43 => Ok(62), // +
47 => Ok(63), // /
48 => Ok(52), // 0
49 => Ok(53), // 1
50 => Ok(54), // 2
51 => Ok(55), // 3
52 => Ok(56), // 4
53 => Ok(57), // 5
54 => Ok(58), // 6
55 => Ok(59), // 7
56 => Ok(60), // 8
57 => Ok(61), // 9
65 => Ok(0), // A
66 => Ok(1), // B
67 => Ok(2), // C
68 => Ok(3), // D
69 => Ok(4), // E
70 => Ok(5), // F
71 => Ok(6), // G
72 => Ok(7), // H
73 => Ok(8), // I
74 => Ok(9), // J
75 => Ok(10), // K
76 => Ok(11), // L
77 => Ok(12), // M
78 => Ok(13), // N
79 => Ok(14), // O
80 => Ok(15), // P
81 => Ok(16), // Q
82 => Ok(17), // R
83 => Ok(18), // S
84 => Ok(19), // T
85 => Ok(20), // U
86 => Ok(21), // V
87 => Ok(22), // W
88 => Ok(23), // X
89 => Ok(24), // Y
90 => Ok(25), // Z
97 => Ok(26), // a
98 => Ok(27), // b
99 => Ok(28), // c
100 => Ok(29), // d
101 => Ok(30), // e
102 => Ok(31), // f
103 => Ok(32), // g
104 => Ok(33), // h
105 => Ok(34), // i
106 => Ok(35), // j
107 => Ok(36), // k
108 => Ok(37), // l
109 => Ok(38), // m
110 => Ok(39), // n
111 => Ok(40), // o
112 => Ok(41), // p
113 => Ok(42), // q
114 => Ok(43), // r
115 => Ok(44), // s
116 => Ok(45), // t
117 => Ok(46), // u
118 => Ok(47), // v
119 => Ok(48), // w
120 => Ok(49), // x
121 => Ok(50), // y
122 => Ok(51), // z
_ => Err(Error::new(ErrorKind::InvalidData, "invalid base64")),
}
}
#[derive(Debug)]
pub struct Decoder {
tmp: [u8; 4],
pub nb: u8,
}
impl Decoder {
pub fn new() -> Decoder {
Decoder { tmp: [0; 4], nb: 0 }
}
}
impl Default for Decoder {
fn default() -> Self {
Self::new()
}
}
pub fn get_decoded_buffer_size(encoded_len: u32) -> u32 {
return ((encoded_len * 3) + (encoded_len % 4)) / 4;
}
pub fn decode_rfc4648(decoder: &mut Decoder, input: &[u8], output: &mut [u8], decoded_bytes: &mut u32) -> Result<()>
{
let mut i = input;
let mut offset = 0;
let mut stop = false;
while !i.is_empty() {
while decoder.nb < 4 {
if !i.is_empty() && (base64_map(i[0]).is_ok() || i[0] == b'=') {
decoder.tmp[decoder.nb as usize] = i[0];
decoder.nb += 1;
} else {
while decoder.nb > 0
&& decoder.nb < 4
{
decoder.tmp[decoder.nb as usize] = b'=';
decoder.nb += 1;
}
stop = true;
break;
}
i = &i[1..];
}
if decoder.nb == 4 {
decoder.tmp[0] = base64_map(decoder.tmp[0])?;
decoder.tmp[1] = base64_map(decoder.tmp[1])?;
if decoder.tmp[2] == b'=' {
output[offset] = (decoder.tmp[0] << 2) | (decoder.tmp[1] >> 4);
offset += 1;
} else {
decoder.tmp[2] = base64_map(decoder.tmp[2])?;
if decoder.tmp[3] == b'=' {
output[offset] = (decoder.tmp[0] << 2) | (decoder.tmp[1] >> 4);
output[offset + 1] = (decoder.tmp[1] << 4) | (decoder.tmp[2] >> 2);
offset += 2;
} else {
decoder.tmp[3] = base64_map(decoder.tmp[3])?;
output[offset] = (decoder.tmp[0] << 2) | (decoder.tmp[1] >> 4);
output[offset + 1] = (decoder.tmp[1] << 4) | (decoder.tmp[2] >> 2);
output[offset + 2] = (decoder.tmp[2] << 6) | decoder.tmp[3];
offset += 3;
}
}
decoder.nb = 0;
}
if stop {
break;
}
}
*decoded_bytes = offset as u32;
return Ok(());
}
pub fn decode_rfc2045(decoder: &mut Decoder, input: &[u8], output: &mut [u8], decoded_bytes: &mut u32) -> Result<()>
{
let mut i = input;
let mut offset = 0;
while !i.is_empty() {
while decoder.nb < 4 && !i.is_empty() {
if base64_map(i[0]).is_ok() || i[0] == b'=' {
decoder.tmp[decoder.nb as usize] = i[0];
decoder.nb += 1;
}
i = &i[1..];
}
if decoder.nb == 4 {
decoder.tmp[0] = base64_map(decoder.tmp[0])?;
decoder.tmp[1] = base64_map(decoder.tmp[1])?;
if decoder.tmp[2] == b'=' {
output[offset] = (decoder.tmp[0] << 2) | (decoder.tmp[1] >> 4);
offset += 1;
} else {
decoder.tmp[2] = base64_map(decoder.tmp[2])?;
if decoder.tmp[3] == b'=' {
output[offset] = (decoder.tmp[0] << 2) | (decoder.tmp[1] >> 4);
output[offset + 1] = (decoder.tmp[1] << 4) | (decoder.tmp[2] >> 2);
offset += 2;
} else {
decoder.tmp[3] = base64_map(decoder.tmp[3])?;
output[offset] = (decoder.tmp[0] << 2) | (decoder.tmp[1] >> 4);
output[offset + 1] = (decoder.tmp[1] << 4) | (decoder.tmp[2] >> 2);
output[offset + 2] = (decoder.tmp[2] << 6) | decoder.tmp[3];
offset += 3;
}
}
decoder.nb = 0;
}
}
*decoded_bytes = offset as u32;
return Ok(());
}

@ -0,0 +1,18 @@
/* Copyright (C) 2024 Open Information Security Foundation
*
* You can copy, redistribute or modify this Program under the terms of
* the GNU General Public License version 2 as published by the Free
* Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* version 2 along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
pub mod base64;

@ -469,7 +469,6 @@ noinst_HEADERS = \
util-action.h \
util-affinity.h \
util-atomic.h \
util-base64.h \
util-bpf.h \
util-buffer.h \
util-byte.h \
@ -1030,7 +1029,6 @@ libsuricata_c_a_SOURCES = \
util-action.c \
util-affinity.c \
util-atomic.c \
util-base64.c \
util-bpf.c \
util-buffer.c \
util-byte.c \

@ -1,4 +1,4 @@
/* Copyright (C) 2017-2019 Open Information Security Foundation
/* Copyright (C) 2017-2024 Open Information Security Foundation
*
* You can copy, redistribute or modify this Program under the terms of
* the GNU General Public License version 2 as published by the Free
@ -27,7 +27,6 @@
#include "datasets-md5.h"
#include "util-thash.h"
#include "util-print.h"
#include "util-base64.h" // decode base64
int Md5StrSet(void *dst, void *src)
{

@ -1,4 +1,4 @@
/* Copyright (C) 2017-2019 Open Information Security Foundation
/* Copyright (C) 2017-2024 Open Information Security Foundation
*
* You can copy, redistribute or modify this Program under the terms of
* the GNU General Public License version 2 as published by the Free
@ -27,7 +27,6 @@
#include "datasets-sha256.h"
#include "util-thash.h"
#include "util-print.h"
#include "util-base64.h" // decode base64
int Sha256StrSet(void *dst, void *src)
{

@ -1,4 +1,4 @@
/* Copyright (C) 2017-2019 Open Information Security Foundation
/* Copyright (C) 2017-2024 Open Information Security Foundation
*
* You can copy, redistribute or modify this Program under the terms of
* the GNU General Public License version 2 as published by the Free
@ -27,7 +27,6 @@
#include "datasets-string.h"
#include "util-thash.h"
#include "util-print.h"
#include "util-base64.h" // decode base64
#include "rust.h"
#if 0

@ -35,7 +35,6 @@
#include "util-conf.h"
#include "util-thash.h"
#include "util-print.h"
#include "util-base64.h" // decode base64
#include "util-byte.h"
#include "util-misc.h"
#include "util-path.h"
@ -518,13 +517,12 @@ static int DatasetLoadString(Dataset *set)
if (r == NULL) {
line[strlen(line) - 1] = '\0';
SCLogDebug("line: '%s'", line);
uint32_t decoded_size = Base64DecodeBufferSize(strlen(line));
// coverity[alloc_strlen : FALSE]
uint8_t decoded[strlen(line)];
uint32_t consumed = 0, num_decoded = 0;
Base64Ecode code = DecodeBase64(decoded, (uint32_t)strlen(line), (const uint8_t *)line,
(uint32_t)strlen(line), &consumed, &num_decoded, Base64ModeStrict);
if (code == BASE64_ECODE_ERR) {
uint8_t decoded[decoded_size];
uint32_t num_decoded =
Base64Decode((const uint8_t *)line, strlen(line), Base64ModeStrict, decoded);
if (num_decoded == 0 && strlen(line) > 0) {
FatalErrorOnInit("bad base64 encoding %s/%s", set->name, set->load);
continue;
}
@ -540,12 +538,11 @@ static int DatasetLoadString(Dataset *set)
*r = '\0';
// coverity[alloc_strlen : FALSE]
uint8_t decoded[strlen(line)];
uint32_t consumed = 0, num_decoded = 0;
Base64Ecode code = DecodeBase64(decoded, (uint32_t)strlen(line), (const uint8_t *)line,
(uint32_t)strlen(line), &consumed, &num_decoded, Base64ModeStrict);
if (code == BASE64_ECODE_ERR) {
uint32_t decoded_size = Base64DecodeBufferSize(strlen(line));
uint8_t decoded[decoded_size];
uint32_t num_decoded =
Base64Decode((const uint8_t *)line, strlen(line), Base64ModeStrict, decoded);
if (num_decoded == 0) {
FatalErrorOnInit("bad base64 encoding %s/%s", set->name, set->load);
continue;
}
@ -1604,16 +1601,16 @@ static int DatasetOpSerialized(Dataset *set, const char *string, DatasetOpFunc D
{
if (set == NULL)
return -1;
if (strlen(string) == 0)
return -1;
switch (set->type) {
case DATASET_TYPE_STRING: {
// coverity[alloc_strlen : FALSE]
uint8_t decoded[strlen(string)];
uint32_t consumed = 0, num_decoded = 0;
Base64Ecode code =
DecodeBase64(decoded, (uint32_t)strlen(string), (const uint8_t *)string,
(uint32_t)strlen(string), &consumed, &num_decoded, Base64ModeStrict);
if (code == BASE64_ECODE_ERR) {
uint32_t decoded_size = Base64DecodeBufferSize(strlen(string));
uint8_t decoded[decoded_size];
uint32_t num_decoded = Base64Decode(
(const uint8_t *)string, strlen(string), Base64ModeStrict, decoded);
if (num_decoded == 0) {
return -2;
}

@ -1,4 +1,4 @@
/* Copyright (C) 2020-2022 Open Information Security Foundation
/* Copyright (C) 2020-2024 Open Information Security Foundation
*
* You can copy, redistribute or modify this Program under the terms of
* the GNU General Public License version 2 as published by the Free
@ -19,10 +19,10 @@
#include "detect.h"
#include "detect-parse.h"
#include "detect-base64-decode.h"
#include "util-base64.h"
#include "util-byte.h"
#include "util-print.h"
#include "detect-engine-build.h"
#include "rust.h"
/* Arbitrary maximum buffer size for decoded base64 data. */
#define BASE64_DECODE_MAX 65535
@ -89,17 +89,18 @@ int DetectBase64DecodeDoMatch(DetectEngineThreadCtx *det_ctx, const Signature *s
decode_len = MIN(payload_len, data->bytes);
DEBUG_VALIDATE_BUG_ON(decode_len < 0);
#if 0
printf("Decoding:\n");
PrintRawDataFp(stdout, payload, decode_len);
#endif
uint32_t consumed = 0, num_decoded = 0;
(void)DecodeBase64(det_ctx->base64_decoded, det_ctx->base64_decoded_len_max, payload,
decode_len, &consumed, &num_decoded, Base64ModeRFC4648);
det_ctx->base64_decoded_len = num_decoded;
SCLogDebug("Decoded %d bytes from base64 data.",
det_ctx->base64_decoded_len);
if (decode_len > 0) {
uint32_t num_decoded =
Base64Decode(payload, decode_len, Base64ModeRFC4648, det_ctx->base64_decoded);
det_ctx->base64_decoded_len = num_decoded;
SCLogDebug("Decoded %d bytes from base64 data.", det_ctx->base64_decoded_len);
}
#if 0
if (det_ctx->base64_decoded_len) {
printf("Decoded data:\n");

@ -34,7 +34,6 @@
#include "detect-transform-base64.h"
#include "util-base64.h"
#include "util-unittest.h"
#include "util-print.h"
@ -119,15 +118,9 @@ static void TransformFromBase64Decode(InspectionBuffer *buffer, void *options)
SCDetectTransformFromBase64Data *b64d = options;
const uint8_t *input = buffer->inspect;
const uint32_t input_len = buffer->inspect_len;
/*
* The output buffer is larger than needed. On average,
* a base64 encoded string is 75% of the encoded
* string.
*/
uint8_t output[input_len];
uint32_t decode_length = input_len;
DetectBase64Mode mode = b64d->mode;
Base64Mode mode = b64d->mode;
uint32_t offset = b64d->offset;
uint32_t nbytes = b64d->nbytes;
@ -149,16 +142,12 @@ static void TransformFromBase64Decode(InspectionBuffer *buffer, void *options)
decode_length = nbytes;
}
// PrintRawDataFp(stdout, input, input_len);
uint32_t decoded_length = 0;
uint32_t consumed = 0;
Base64Ecode code =
DecodeBase64(output, input_len, input, decode_length, &consumed, &decoded_length, mode);
if (code != BASE64_ECODE_ERR) {
// PrintRawDataFp(stdout, output, decoded_length);
if (decoded_length) {
InspectionBufferCopy(buffer, output, decoded_length);
}
uint32_t decoded_size = Base64DecodeBufferSize(decode_length);
uint8_t decoded[decoded_size];
uint32_t num_decoded = Base64Decode((const uint8_t *)input, decode_length, mode, decoded);
if (num_decoded > 0) {
// PrintRawDataFp(stdout, output, b64data->decoded_len);
InspectionBufferCopy(buffer, decoded, num_decoded);
}
}

@ -1,4 +1,4 @@
/* Copyright (C) 2013-2022 Open Information Security Foundation
/* Copyright (C) 2013-2024 Open Information Security Foundation
*
* You can copy, redistribute or modify this Program under the terms of
* the GNU General Public License version 2 as published by the Free
@ -78,7 +78,6 @@
#include "util-memcmp.h"
#include "util-misc.h"
#include "util-signal.h"
#include "util-base64.h"
#include "reputation.h"
#include "util-atomic.h"
@ -181,7 +180,6 @@ static void RegisterUnittests(void)
ThreadMacrosRegisterTests();
UtilSpmSearchRegistertests();
UtilActionRegisterTests();
Base64RegisterTests();
SCClassConfRegisterTests();
SCThresholdConfRegisterTests();
SCRConfRegisterTests();

@ -6,7 +6,7 @@
#include "suricata-common.h"
#include "suricata.h"
#include "util-base64.h"
#include "rust.h"
#define BLK_SIZE 2
@ -14,20 +14,16 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size);
static int initialized = 0;
static void Base64FuzzTest(const uint8_t *src, size_t len, size_t dest_size)
static void Base64FuzzTest(const uint8_t *src, size_t len)
{
uint8_t *dest = malloc(dest_size);
if (dest == NULL)
return;
uint32_t decoded_len = Base64DecodeBufferSize(len);
uint8_t *decoded = SCCalloc(decoded_len, sizeof(uint8_t));
for (uint8_t mode = Base64ModeRelax; mode <= Base64ModeRFC4648; mode++) {
uint32_t consumed_bytes = 0;
uint32_t decoded_bytes = 0;
DecodeBase64(dest, dest_size, src, len, &consumed_bytes, &decoded_bytes, mode);
for (uint8_t mode = Base64ModeRFC2045; mode <= Base64ModeStrict; mode++) {
(void)Base64Decode(src, len, mode, decoded);
}
free(dest);
SCFree(decoded);
}
int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
@ -45,8 +41,7 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
if (size < BLK_SIZE)
return 0;
uint32_t dest_size = (uint32_t)(data[0] << 8) | (uint32_t)(data[1]);
Base64FuzzTest(data + BLK_SIZE, size - BLK_SIZE, dest_size);
Base64FuzzTest(data, size);
return 0;
}

@ -1,506 +0,0 @@
/* Copyright (C) 2007-2024 Open Information Security Foundation
*
* You can copy, redistribute or modify this Program under the terms of
* the GNU General Public License version 2 as published by the Free
* Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* version 2 along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
/**
* \file
*
* \author David Abarbanel <david.abarbanel@baesystems.com>
*
*/
#include "util-base64.h"
#include "util-debug.h"
#include "util-validate.h"
#include "util-unittest.h"
/* Constants */
#define BASE64_TABLE_MAX 122
/* Base64 character to index conversion table */
/* Characters are mapped as "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" */
static const int b64table[] = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, 62, -1, -1, -1, 63, 52, 53,
54, 55, 56, 57, 58, 59, 60, 61, -1, -1,
-1, -1, -1, -1, -1, 0, 1, 2, 3, 4,
5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
25, -1, -1, -1, -1, -1, -1, 26, 27, 28,
29, 30, 31, 32, 33, 34, 35, 36, 37, 38,
39, 40, 41, 42, 43, 44, 45, 46, 47, 48,
49, 50, 51 };
/**
* \brief Gets a base64-decoded value from an encoded character
*
* \param c The encoded character
*
* \return The decoded value (0 or above), or -1 if the parameter is invalid
*/
static inline int GetBase64Value(uint8_t c)
{
int val = -1;
/* Pull from conversion table */
if (c <= BASE64_TABLE_MAX) {
val = b64table[(int) c];
}
return val;
}
/**
* \brief Checks if the given char in a byte array is Base64 alphabet
*
* \param Char that needs to be checked
*
* \return True if the char was Base64 alphabet, False otherwise
*/
bool IsBase64Alphabet(uint8_t encoded_byte)
{
if (GetBase64Value(encoded_byte) < 0 && encoded_byte != '=') {
return false;
}
return true;
}
/**
* \brief Decodes a 4-byte base64-encoded block into a 3-byte ascii-encoded block
*
* \param ascii the 3-byte ascii output block
* \param b64 the 4-byte base64 input block
*
* \return none
*/
static inline void DecodeBase64Block(uint8_t ascii[ASCII_BLOCK], uint8_t b64[B64_BLOCK])
{
ascii[0] = (uint8_t) (b64[0] << 2) | (b64[1] >> 4);
ascii[1] = (uint8_t) (b64[1] << 4) | (b64[2] >> 2);
ascii[2] = (uint8_t) (b64[2] << 6) | (b64[3]);
}
/**
* \brief Decode a base64 encoded string as per RFC 2045.
* RFC 2045 states that any characters that do not fall under the Base64
* alphabet must be skipped by the decoding software.
* Following are some important considerations:
* 1. This Decoding algorithm is used by MIME parser currently.
* 2. The number of decoded bytes are constrained by the destination buffer size.
* 3. The leftover bytes are not handled by the decoder but the caller.
*
* \param dest destination buffer
* \param dest_size destination buffer size
* \param src base64 encoded string
* \param len length of the base64 encoded string
* \param consumed_bytes number of bytes successfully consumed by the decoder
* \param decoded_bytes number of bytes successfully decoded by the decoder
*
* \return Base64Ecode BASE64_ECODE_OK if all went well
* BASE64_ECODE_BUF if destination buffer got full before all could be decoded
*/
static inline Base64Ecode DecodeBase64RFC2045(uint8_t *dest, uint32_t dest_size, const uint8_t *src,
uint32_t len, uint32_t *consumed_bytes, uint32_t *decoded_bytes)
{
int val;
uint32_t padding = 0, bbidx = 0, non_b64_chars = 0;
uint8_t *dptr = dest;
uint8_t b64[B64_BLOCK] = { 0, 0, 0, 0 };
for (uint32_t i = 0; i < len; i++) {
val = GetBase64Value(src[i]);
if (val < 0) {
if (src[i] != '=') {
non_b64_chars++;
continue;
} else {
padding++;
}
}
/* For each alpha-numeric letter in the source array, find the numeric value */
b64[bbidx++] = val > 0 ? (uint8_t)val : 0;
/* Decode every 4 base64 bytes into 3 ascii bytes */
if (bbidx == B64_BLOCK) {
/* For every 4 bytes, add 3 bytes but deduct the '=' padded blocks */
uint32_t numDecoded_blk = ASCII_BLOCK - (padding < B64_BLOCK ? padding : ASCII_BLOCK);
if (dest_size - *decoded_bytes < ASCII_BLOCK)
return BASE64_ECODE_BUF;
/* Decode base-64 block into ascii block and move pointer */
DecodeBase64Block(dptr, b64);
dptr += numDecoded_blk;
*decoded_bytes += numDecoded_blk;
/* Reset base-64 block and index */
bbidx = 0;
padding = 0;
*consumed_bytes += B64_BLOCK + non_b64_chars;
non_b64_chars = 0;
memset(&b64, 0, sizeof(b64));
}
}
DEBUG_VALIDATE_BUG_ON(*consumed_bytes > len);
DEBUG_VALIDATE_BUG_ON(bbidx == B64_BLOCK);
/* Any leftover bytes must be handled by the caller */
return BASE64_ECODE_OK;
}
/**
* \brief Decode a base64 encoded string as per RFC 4648.
* RFC 4648 states that if a character is encountered that does not fall under
* the Base64 alphabet, the decoding software should stop processing the string further.
* Following are some important considerations:
* 1. This Decoding algorithm is used by base64_decode keyword currently.
* 2. This Decoding algorithm in strict mode is used by datasets currently.
* 3. The number of decoded bytes are constrained by the destination buffer size.
* 4. The leftover bytes are handled by the decoder.
*
* \param dest destination buffer
* \param dest_size destination buffer size
* \param src base64 encoded string
* \param len length of the base64 encoded string
* \param consumed_bytes number of bytes successfully consumed by the decoder
* \param decoded_bytes number of bytes successfully decoded by the decoder
* \param strict whether an invalid base64 encoding should be strictly rejected
*
* \return Base64Ecode BASE64_ECODE_OK if all went well
* BASE64_ECODE_BUF if destination buffer got full before all could be decoded
* BASE64_ECODE_ERR if an invalid char was found in strict mode or nothing was
* decoded
*/
static inline Base64Ecode DecodeBase64RFC4648(uint8_t *dest, uint32_t dest_size, const uint8_t *src,
uint32_t len, uint32_t *consumed_bytes, uint32_t *decoded_bytes, bool strict)
{
int val;
uint32_t padding = 0, bbidx = 0;
uint8_t *dptr = dest;
uint8_t b64[B64_BLOCK] = { 0, 0, 0, 0 };
for (uint32_t i = 0; i < len; i++) {
val = GetBase64Value(src[i]);
if (val < 0) {
if (src[i] != '=') {
if (strict) {
*decoded_bytes = 0;
return BASE64_ECODE_ERR;
}
break;
}
padding++;
}
/* For each alpha-numeric letter in the source array, find the numeric value */
b64[bbidx++] = (val > 0 ? (uint8_t)val : 0);
/* Decode every 4 base64 bytes into 3 ascii bytes */
if (bbidx == B64_BLOCK) {
/* For every 4 bytes, add 3 bytes but deduct the '=' padded blocks */
uint32_t numDecoded_blk = ASCII_BLOCK - (padding < B64_BLOCK ? padding : ASCII_BLOCK);
if (dest_size - *decoded_bytes < ASCII_BLOCK)
return BASE64_ECODE_BUF;
/* Decode base-64 block into ascii block and move pointer */
DecodeBase64Block(dptr, b64);
dptr += numDecoded_blk;
*decoded_bytes += numDecoded_blk;
/* Reset base-64 block and index */
bbidx = 0;
padding = 0;
*consumed_bytes += B64_BLOCK;
memset(&b64, 0, sizeof(b64));
}
}
DEBUG_VALIDATE_BUG_ON(bbidx == B64_BLOCK);
/* Handle any leftover bytes by adding padding to them as long as they do not
* violate the destination buffer size */
if (bbidx > 0) {
/*
* --------------------
* | bbidx | padding |
* --------------------
* | 1 | 2 |
* | 2 | 2 |
* | 3 | 1 |
* --------------------
* Note: Padding for 1 byte is set to 2 to have at least one
* decoded byte while calculating numDecoded_blk
* This does not affect the decoding as the b64 array is already
* populated with all padding bytes unless overwritten.
* */
padding = bbidx > 1 ? B64_BLOCK - bbidx : 2;
uint32_t numDecoded_blk = ASCII_BLOCK - padding;
if (dest_size - *decoded_bytes < ASCII_BLOCK)
return BASE64_ECODE_BUF;
/* Decode base-64 block into ascii block and move pointer */
DecodeBase64Block(dptr, b64);
*decoded_bytes += numDecoded_blk;
/* Consumed bytes should not have the padding bytes added by us */
*consumed_bytes += bbidx;
}
if (*decoded_bytes == 0)
return BASE64_ECODE_ERR;
DEBUG_VALIDATE_BUG_ON(*consumed_bytes > len);
return BASE64_ECODE_OK;
}
/**
* \brief Decodes a base64-encoded string buffer into an ascii-encoded byte buffer
*
* \param dest The destination byte buffer
* \param dest_size The destination byte buffer size
* \param src The source string
* \param len The length of the source string
* \param consumed_bytes The bytes that were actually processed/consumed
* \param decoded_bytes The bytes that were decoded
* \param mode The mode in which decoding should happen
*
* \return Error code indicating success or failures with parsing
*/
Base64Ecode DecodeBase64(uint8_t *dest, uint32_t dest_size, const uint8_t *src, uint32_t len,
uint32_t *consumed_bytes, uint32_t *decoded_bytes, DetectBase64Mode mode)
{
*decoded_bytes = 0;
Base64Ecode ret = BASE64_ECODE_OK;
switch (mode) {
case Base64ModeRFC4648:
ret = DecodeBase64RFC4648(
dest, dest_size, src, len, consumed_bytes, decoded_bytes, false);
break;
case Base64ModeRFC2045:
ret = DecodeBase64RFC2045(dest, dest_size, src, len, consumed_bytes, decoded_bytes);
break;
case Base64ModeStrict:
ret = DecodeBase64RFC4648(
dest, dest_size, src, len, consumed_bytes, decoded_bytes, true);
break;
default:
return BASE64_ECODE_ERR;
}
return ret;
}
#ifdef UNITTESTS
#define TEST_RFC2045(src, fin_str, dest_size, exp_decoded, exp_consumed, ecode) \
{ \
uint32_t consumed_bytes = 0, num_decoded = 0; \
uint8_t dst[dest_size]; \
Base64Ecode code = DecodeBase64(dst, dest_size, (const uint8_t *)src, strlen(src), \
&consumed_bytes, &num_decoded, Base64ModeRFC2045); \
FAIL_IF(code != ecode); \
FAIL_IF(memcmp(dst, fin_str, strlen(fin_str)) != 0); \
FAIL_IF(num_decoded != exp_decoded); \
FAIL_IF(consumed_bytes != exp_consumed); \
}
#define TEST_RFC4648(src, fin_str, dest_size, exp_decoded, exp_consumed, ecode) \
{ \
uint32_t consumed_bytes = 0, num_decoded = 0; \
uint8_t dst[dest_size]; \
Base64Ecode code = DecodeBase64(dst, dest_size, (const uint8_t *)src, strlen(src), \
&consumed_bytes, &num_decoded, Base64ModeRFC4648); \
FAIL_IF(code != ecode); \
FAIL_IF(memcmp(dst, fin_str, strlen(fin_str)) != 0); \
FAIL_IF(num_decoded != exp_decoded); \
FAIL_IF(consumed_bytes != exp_consumed); \
}
static int B64DecodeCompleteString(void)
{
/*
* SGVsbG8gV29ybGR6 : Hello Worldz
* */
const char *src = "SGVsbG8gV29ybGR6";
const char *fin_str = "Hello Worldz";
TEST_RFC2045(src, fin_str, 12, 12, 16, BASE64_ECODE_OK);
PASS;
}
static int B64DecodeInCompleteString(void)
{
/*
* SGVsbG8gV29ybGR6 : Hello Worldz
* */
const char *src = "SGVsbG8gV29ybGR";
const char *fin_str = "Hello Wor";
TEST_RFC2045(src, fin_str, 9, 9, 12, BASE64_ECODE_OK);
PASS;
}
static int B64DecodeCompleteStringWSp(void)
{
/*
* SGVsbG8gV29ybGQ= : Hello World
* */
const char *src = "SGVs bG8 gV29y bGQ=";
const char *fin_str = "Hello World";
TEST_RFC2045(src, fin_str, 14, 11, 19, BASE64_ECODE_OK);
PASS;
}
static int B64DecodeInCompleteStringWSp(void)
{
/*
* SGVsbG8gV29ybGQ= : Hello World
* Special handling for this case (sp in remainder) done in ProcessBase64Remainder
* */
const char *src = "SGVs bG8 gV29y bGQ";
const char *fin_str = "Hello Wor";
TEST_RFC2045(src, fin_str, 9, 9, 14, BASE64_ECODE_OK);
PASS;
}
static int B64DecodeStringBiggerThanBuffer(void)
{
/*
* SGVsbG8gV29ybGQ= : Hello World
* */
const char *src = "SGVs bG8 gV29y bGQ=";
const char *fin_str = "Hello Wor";
TEST_RFC2045(src, fin_str, 10, 9, 14, BASE64_ECODE_BUF);
PASS;
}
static int B64DecodeStringEndingSpaces(void)
{
const char *src = "0YPhA d H";
uint32_t consumed_bytes = 0, num_decoded = 0;
uint8_t dst[10];
Base64Ecode code = DecodeBase64(dst, sizeof(dst), (const uint8_t *)src, 9, &consumed_bytes,
&num_decoded, Base64ModeRFC2045);
FAIL_IF(code != BASE64_ECODE_OK);
FAIL_IF(num_decoded != 3);
FAIL_IF(consumed_bytes != 4);
PASS;
}
static int B64TestVectorsRFC2045(void)
{
const char *src1 = "";
const char *fin_str1 = "";
const char *src2 = "Zg==";
const char *fin_str2 = "f";
const char *src3 = "Zm8=";
const char *fin_str3 = "fo";
const char *src4 = "Zm9v";
const char *fin_str4 = "foo";
const char *src5 = "Zm9vYg==";
const char *fin_str5 = "foob";
const char *src6 = "Zm9vYmE=";
const char *fin_str6 = "fooba";
const char *src7 = "Zm9vYmFy";
const char *fin_str7 = "foobar";
const char *src8 = "Zm 9v Ym Fy";
const char *fin_str8 = "foobar";
const char *src9 = "Zm$9vYm.Fy";
const char *fin_str9 = "foobar";
const char *src10 = "Y21Wd2IzSjBaVzFoYVd4bWNtRjFaRUJoZEc4dVoyOTJMbUYxOmpqcHh4b3Rhb2w%5";
const char *fin_str10 = "cmVwb3J0ZW1haWxmcmF1ZEBhdG8uZ292LmF1:jjpxxotaol9";
const char *src11 = "Zm 9v Ym Fy 7fy";
const char *fin_str11 = "foobar";
TEST_RFC2045(src1, fin_str1, ASCII_BLOCK * 2, strlen(fin_str1), strlen(src1), BASE64_ECODE_OK);
TEST_RFC2045(src2, fin_str2, ASCII_BLOCK * 2, strlen(fin_str2), strlen(src2), BASE64_ECODE_OK);
TEST_RFC2045(src3, fin_str3, ASCII_BLOCK * 2, strlen(fin_str3), strlen(src3), BASE64_ECODE_OK);
TEST_RFC2045(src4, fin_str4, ASCII_BLOCK * 2, strlen(fin_str4), strlen(src4), BASE64_ECODE_OK);
TEST_RFC2045(src5, fin_str5, ASCII_BLOCK * 2, strlen(fin_str5), strlen(src5), BASE64_ECODE_OK);
TEST_RFC2045(src6, fin_str6, ASCII_BLOCK * 2, strlen(fin_str6), strlen(src6), BASE64_ECODE_OK);
TEST_RFC2045(src7, fin_str7, ASCII_BLOCK * 2, strlen(fin_str7), strlen(src7), BASE64_ECODE_OK);
TEST_RFC2045(src8, fin_str8, ASCII_BLOCK * 2, strlen(fin_str8), strlen(src8), BASE64_ECODE_OK);
TEST_RFC2045(src9, fin_str9, ASCII_BLOCK * 2, strlen(fin_str9), strlen(src9), BASE64_ECODE_OK);
TEST_RFC2045(src10, fin_str10, 50, 48, 65, BASE64_ECODE_OK);
TEST_RFC2045(src11, fin_str11, ASCII_BLOCK * 2, 6, 11, BASE64_ECODE_OK);
PASS;
}
static int B64TestVectorsRFC4648(void)
{
const char *src1 = "";
const char *fin_str1 = "";
const char *src2 = "Zg==";
const char *fin_str2 = "f";
const char *src3 = "Zm8=";
const char *fin_str3 = "fo";
const char *src4 = "Zm9v";
const char *fin_str4 = "foo";
const char *src5 = "Zm9vYg==";
const char *fin_str5 = "foob";
const char *src6 = "Zm9vYmE=";
const char *fin_str6 = "fooba";
const char *src7 = "Zm9vYmFy";
const char *fin_str7 = "foobar";
const char *src8 = "Zm 9v Ym Fy";
const char *fin_str8 = "f";
const char *src9 = "Zm$9vYm.Fy";
const char *fin_str9 = "f";
const char *src10 = "Y21Wd2IzSjBaVzFoYVd4bWNtRjFaRUJoZEc4dVoyOTJMbUYxOmpqcHh4b3Rhb2w%3D";
const char *fin_str10 = "cmVwb3J0ZW1haWxmcmF1ZEBhdG8uZ292LmF1:jjpxxotaol";
const char *src11 = "Zm9vYg==";
const char *fin_str11 = "foo";
TEST_RFC4648(src1, fin_str1, ASCII_BLOCK * 2, strlen(fin_str1), strlen(src1), BASE64_ECODE_ERR);
TEST_RFC4648(src2, fin_str2, ASCII_BLOCK * 2, strlen(fin_str2), strlen(src2), BASE64_ECODE_OK);
TEST_RFC4648(src3, fin_str3, ASCII_BLOCK * 2, strlen(fin_str3), strlen(src3), BASE64_ECODE_OK);
TEST_RFC4648(src4, fin_str4, ASCII_BLOCK * 2, strlen(fin_str4), strlen(src4), BASE64_ECODE_OK);
TEST_RFC4648(src5, fin_str5, ASCII_BLOCK * 2, strlen(fin_str5), strlen(src5), BASE64_ECODE_OK);
TEST_RFC4648(src6, fin_str6, ASCII_BLOCK * 2, strlen(fin_str6), strlen(src6), BASE64_ECODE_OK);
TEST_RFC4648(src7, fin_str7, ASCII_BLOCK * 2, strlen(fin_str7), strlen(src7), BASE64_ECODE_OK);
TEST_RFC4648(src8, fin_str8, ASCII_BLOCK * 2, 1 /* f */, 2 /* Zm */, BASE64_ECODE_OK);
TEST_RFC4648(src9, fin_str9, ASCII_BLOCK * 2, 1 /* f */, 2 /* Zm */, BASE64_ECODE_OK);
TEST_RFC4648(src10, fin_str10, 48, 47, 63, BASE64_ECODE_OK);
TEST_RFC4648(src11, fin_str11, 3, 3, 4, BASE64_ECODE_BUF);
PASS;
}
void Base64RegisterTests(void)
{
UtRegisterTest("B64DecodeCompleteStringWSp", B64DecodeCompleteStringWSp);
UtRegisterTest("B64DecodeInCompleteStringWSp", B64DecodeInCompleteStringWSp);
UtRegisterTest("B64DecodeCompleteString", B64DecodeCompleteString);
UtRegisterTest("B64DecodeInCompleteString", B64DecodeInCompleteString);
UtRegisterTest("B64DecodeStringBiggerThanBuffer", B64DecodeStringBiggerThanBuffer);
UtRegisterTest("B64DecodeStringEndingSpaces", B64DecodeStringEndingSpaces);
UtRegisterTest("B64TestVectorsRFC2045", B64TestVectorsRFC2045);
UtRegisterTest("B64TestVectorsRFC4648", B64TestVectorsRFC4648);
}
#endif

@ -1,50 +0,0 @@
/* Copyright (C) 2007-2012 Open Information Security Foundation
*
* You can copy, redistribute or modify this Program under the terms of
* the GNU General Public License version 2 as published by the Free
* Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* version 2 along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
/**
* \file
*
* \author David Abarbanel <david.abarbanel@baesystems.com>
*
*/
#ifndef SURICATA_UTIL_BASE64_H_
#define SURICATA_UTIL_BASE64_H_
#include "suricata-common.h"
#include "rust.h"
/* Constants */
#define ASCII_BLOCK 3
#define B64_BLOCK 4
typedef enum {
BASE64_ECODE_ERR = -1,
BASE64_ECODE_OK = 0,
BASE64_ECODE_BUF,
} Base64Ecode;
/* Function prototypes */
Base64Ecode DecodeBase64(uint8_t *dest, uint32_t dest_size, const uint8_t *src, uint32_t len,
uint32_t *consumed_bytes, uint32_t *decoded_bytes, DetectBase64Mode mode);
bool IsBase64Alphabet(uint8_t encoded_byte);
#endif
#ifdef UNITTESTS
void Base64RegisterTests(void);
#endif
Loading…
Cancel
Save