Coverage Report

Created: 2026-09-06 07:25

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/suricata8/rust/src/ffi/base64.rs
Line
Count
Source
1
/* Copyright (C) 2021-2024 Open Information Security Foundation
2
 *
3
 * You can copy, redistribute or modify this Program under the terms of
4
 * the GNU General Public License version 2 as published by the Free
5
 * Software Foundation.
6
 *
7
 * This program is distributed in the hope that it will be useful,
8
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10
 * GNU General Public License for more details.
11
 *
12
 * You should have received a copy of the GNU General Public License
13
 * version 2 along with this program; if not, write to the Free Software
14
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
15
 * 02110-1301, USA.
16
 */
17
18
use crate::utils::base64::{decode_rfc2045, decode_rfc4648, get_decoded_buffer_size, Decoder};
19
use base64::{
20
    engine::general_purpose::{STANDARD, STANDARD_NO_PAD},
21
    Engine,
22
};
23
use libc::c_ulong;
24
use std::os::raw::c_uchar;
25
26
#[repr(C)]
27
#[allow(non_camel_case_types)]
28
pub enum SCBase64ReturnCode {
29
    SC_BASE64_OK = 0,
30
    SC_BASE64_INVALID_ARG,
31
    SC_BASE64_OVERFLOW,
32
}
33
34
#[repr(u8)]
35
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
36
pub enum SCBase64Mode {
37
    /* If the following strings were to be passed to the decoder with RFC2045 mode,
38
     * the results would be as follows. See the unittest B64TestVectorsRFC2045 in
39
     * src/util-base64.c
40
     *
41
     * BASE64("") = ""
42
     * BASE64("f") = "Zg=="
43
     * BASE64("fo") = "Zm8="
44
     * BASE64("foo") = "Zm9v"
45
     * BASE64("foob") = "Zm9vYg=="
46
     * BASE64("fooba") = "Zm9vYmE="
47
     * BASE64("foobar") = "Zm9vYmFy"
48
     * BASE64("foobar") = "Zm 9v Ym Fy"   <-- Notice how the spaces are ignored
49
     * BASE64("foobar") = "Zm$9vYm.Fy"    # According to RFC 2045, All line breaks or *other
50
     * characters* not found in base64 alphabet must be ignored by decoding software
51
     * */
52
    SCBase64ModeRFC2045 = 0, /* SPs are allowed during transfer but must be skipped by Decoder */
53
    SCBase64ModeStrict,
54
    /* If the following strings were to be passed to the decoder with RFC4648 mode,
55
     * the results would be as follows. See the unittest B64TestVectorsRFC4648 in
56
     * src/util-base64.c
57
     *
58
     * BASE64("") = ""
59
     * BASE64("f") = "Zg=="
60
     * BASE64("fo") = "Zm8="
61
     * BASE64("foo") = "Zm9v"
62
     * BASE64("foob") = "Zm9vYg=="
63
     * BASE64("fooba") = "Zm9vYmE="
64
     * BASE64("foobar") = "Zm9vYmFy"
65
     * BASE64("f") = "Zm 9v Ym Fy"   <-- Notice how the processing stops once space is encountered
66
     * BASE64("f") = "Zm$9vYm.Fy"    <-- Notice how the processing stops once an invalid char is
67
     * encountered
68
     * */
69
    SCBase64ModeRFC4648, /* reject the encoded data if it contains characters outside the base alphabet */
70
71
    /// Standard base64 without padding, and strict about it.
72
    SCBase64ModeNoPad,
73
74
    /// Standard base64 with optional padding: decode only.
75
    SCBase64ModePadOpt,
76
}
77
78
#[no_mangle]
79
1.01k
pub unsafe extern "C" fn SCBase64DecodeBufferSize(input_len: u32) -> u32 {
80
1.01k
    return get_decoded_buffer_size(input_len);
81
1.01k
}
82
83
/// Base64 decode a buffer.
84
///
85
/// This method exposes the Rust base64 decoder to C and should not be called from
86
/// Rust code.
87
///
88
/// It allows decoding in the modes described by ``SCBase64Mode`` enum.
89
#[no_mangle]
90
2.02k
pub unsafe extern "C" fn SCBase64Decode(
91
2.02k
    input: *const u8, len: usize, mode: SCBase64Mode, output: *mut u8,
92
2.02k
) -> u32 {
93
2.02k
    if input.is_null() || len == 0 {
94
0
        return 0;
95
2.02k
    }
96
97
2.02k
    let in_vec = build_slice!(input, len);
98
2.02k
    let out_vec = std::slice::from_raw_parts_mut(output, len);
99
2.02k
    let mut num_decoded: u32 = 0;
100
2.02k
    let mut decoder = Decoder::new();
101
2.02k
    match mode {
102
        SCBase64Mode::SCBase64ModeRFC2045 => {
103
1.01k
            if decode_rfc2045(&mut decoder, in_vec, out_vec, &mut num_decoded).is_err() {
104
79
                debug_validate_bug_on!(num_decoded >= len as u32);
105
79
                return num_decoded;
106
935
            }
107
        }
108
        SCBase64Mode::SCBase64ModeRFC4648 => {
109
0
            if decode_rfc4648(&mut decoder, in_vec, out_vec, &mut num_decoded).is_err() {
110
0
                debug_validate_bug_on!(num_decoded >= len as u32);
111
0
                return num_decoded;
112
0
            }
113
        }
114
        SCBase64Mode::SCBase64ModeStrict => {
115
1.01k
            if let Ok(decoded_len) = STANDARD.decode_slice(in_vec, out_vec) {
116
454
                num_decoded = decoded_len as u32;
117
560
            }
118
        }
119
        SCBase64Mode::SCBase64ModeNoPad => {
120
0
            if let Ok(decoded_len) = STANDARD_NO_PAD.decode_slice(in_vec, out_vec) {
121
0
                num_decoded = decoded_len as u32;
122
0
            }
123
        }
124
        SCBase64Mode::SCBase64ModePadOpt => {
125
0
            let config = base64::engine::GeneralPurposeConfig::new()
126
0
                .with_decode_padding_mode(base64::engine::DecodePaddingMode::Indifferent);
127
0
            let decoder = base64::engine::GeneralPurpose::new(&base64::alphabet::STANDARD, config);
128
0
            if let Ok(decoded_len) = decoder.decode_slice(in_vec, out_vec) {
129
0
                num_decoded = decoded_len as u32;
130
0
            }
131
        }
132
    }
133
134
1.94k
    debug_validate_bug_on!(num_decoded >= len as u32);
135
1.94k
    return num_decoded;
136
2.02k
}
137
138
/// Base64 encode a buffer with a provided mode.
139
///
140
/// This method exposes the Rust base64 encoder to C and should not be called from
141
/// Rust code.
142
///
143
/// The output parameter must be an allocated buffer of at least the size returned
144
/// from SCBase64EncodeBufferSize for the input_len, and this length must be provided
145
/// in the output_len variable.
146
#[no_mangle]
147
pub unsafe extern "C" fn SCBase64EncodeWithMode(
148
    input: *const u8, input_len: c_ulong, output: *mut c_uchar, output_len: *mut c_ulong,
149
    mode: SCBase64Mode,
150
) -> SCBase64ReturnCode {
151
    if input.is_null() || output.is_null() || output_len.is_null() {
152
        return SCBase64ReturnCode::SC_BASE64_INVALID_ARG;
153
    }
154
    let input = std::slice::from_raw_parts(input, input_len as usize);
155
    let encoded = match mode {
156
        SCBase64Mode::SCBase64ModeNoPad => STANDARD_NO_PAD.encode(input),
157
        _ => STANDARD.encode(input),
158
    };
159
    if encoded.len() + 1 > *output_len as usize {
160
        return SCBase64ReturnCode::SC_BASE64_OVERFLOW;
161
    }
162
    let output = std::slice::from_raw_parts_mut(&mut *output, *output_len as usize);
163
    output[0..encoded.len()].copy_from_slice(encoded.as_bytes());
164
    output[encoded.len()] = 0;
165
    *output_len = encoded.len() as c_ulong;
166
    SCBase64ReturnCode::SC_BASE64_OK
167
}
168
169
/// Base64 encode a buffer.
170
///
171
/// This method exposes the Rust base64 encoder to C and should not be called from
172
/// Rust code.
173
///
174
/// The output parameter must be an allocated buffer of at least the size returned
175
/// from SCBase64EncodeBufferSize for the input_len, and this length must be provided
176
/// in the output_len variable.
177
#[no_mangle]
178
0
pub unsafe extern "C" fn SCBase64Encode(
179
0
    input: *const u8, input_len: c_ulong, output: *mut c_uchar, output_len: *mut c_ulong,
180
0
) -> SCBase64ReturnCode {
181
0
    SCBase64EncodeWithMode(
182
0
        input,
183
0
        input_len,
184
0
        output,
185
0
        output_len,
186
0
        SCBase64Mode::SCBase64ModeStrict,
187
    )
188
0
}
189
190
/// Ratio of output bytes to input bytes for Base64 Encoding is 4:3, hence the
191
/// required output bytes are 4 * ceil(input_len / 3) and an additional byte for
192
/// storing the NULL pointer.
193
#[no_mangle]
194
pub extern "C" fn SCBase64EncodeBufferSize(len: c_ulong) -> c_ulong {
195
    (4 * ((len) + 2) / 3) + 1
196
}