Coverage Report

Created: 2026-09-06 07:25

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/suricata8/rust/src/detect/transforms/hash.rs
Line
Count
Source
1
/* Copyright (C) 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::detect::SIGMATCH_NOOPT;
19
use suricata_sys::sys::{
20
    DetectEngineCtx, DetectEngineThreadCtx, InspectionBuffer, SCDetectHelperTransformRegister,
21
    SCDetectSignatureAddTransform, SCTransformTableElmt, Signature, SCInspectionBufferCheckAndExpand,
22
    SCInspectionBufferTruncate,
23
};
24
25
use crate::ffi::hashing::{G_DISABLE_HASHING, SC_SHA1_LEN, SC_SHA256_LEN};
26
use digest::{Digest, Update};
27
use md5::Md5;
28
use sha1::Sha1;
29
use sha2::Sha256;
30
31
use std::os::raw::{c_int, c_void};
32
use std::ptr;
33
34
static mut G_TRANSFORM_MD5_ID: c_int = 0;
35
static mut G_TRANSFORM_SHA1_ID: c_int = 0;
36
static mut G_TRANSFORM_SHA256_ID: c_int = 0;
37
38
const SC_MD5_LEN: usize = 16;
39
40
313
unsafe extern "C" fn md5_setup(
41
313
    _de: *mut DetectEngineCtx, s: *mut Signature, _raw: *const std::os::raw::c_char,
42
313
) -> c_int {
43
313
    if G_DISABLE_HASHING {
44
0
        SCLogError!("MD5 hashing has been disabled, needed for to_md5 keyword");
45
0
        return -1;
46
313
    }
47
313
    return SCDetectSignatureAddTransform(s, G_TRANSFORM_MD5_ID, ptr::null_mut());
48
313
}
49
50
0
fn md5_transform_do(input: &[u8], output: &mut [u8]) {
51
0
    Md5::new().chain(input).finalize_into(output.into());
52
0
}
53
54
0
unsafe extern "C" fn md5_transform(
55
0
    _det: *mut DetectEngineThreadCtx, buffer: *mut InspectionBuffer, _ctx: *mut c_void,
56
0
) {
57
0
    let input = (*buffer).inspect;
58
0
    let input_len = (*buffer).inspect_len;
59
0
    if input.is_null() || input_len == 0 {
60
0
        return;
61
0
    }
62
0
    let input = build_slice!(input, input_len as usize);
63
64
0
    let output = SCInspectionBufferCheckAndExpand(buffer, SC_MD5_LEN as u32);
65
0
    if output.is_null() {
66
        // allocation failure
67
0
        return;
68
0
    }
69
0
    let output = std::slice::from_raw_parts_mut(output, SC_MD5_LEN);
70
71
0
    md5_transform_do(input, output);
72
73
0
    SCInspectionBufferTruncate(buffer, SC_MD5_LEN as u32);
74
0
}
75
76
#[no_mangle]
77
39
pub unsafe extern "C" fn DetectTransformMd5Register() {
78
39
    let kw = SCTransformTableElmt {
79
39
        name: b"to_md5\0".as_ptr() as *const libc::c_char,
80
39
        desc: b"convert to md5 hash of the buffer\0".as_ptr() as *const libc::c_char,
81
39
        url: b"/rules/transforms.html#to-md5\0".as_ptr() as *const libc::c_char,
82
39
        Setup: Some(md5_setup),
83
39
        flags: SIGMATCH_NOOPT,
84
39
        Transform: Some(md5_transform),
85
39
        Free: None,
86
39
        TransformValidate: None,
87
39
        TransformId: None,
88
39
    };
89
39
    G_TRANSFORM_MD5_ID = SCDetectHelperTransformRegister(&kw);
90
39
    if G_TRANSFORM_MD5_ID < 0 {
91
0
        SCLogWarning!("Failed registering transform md5");
92
39
    }
93
39
}
94
95
639
unsafe extern "C" fn sha1_setup(
96
639
    _de: *mut DetectEngineCtx, s: *mut Signature, _raw: *const std::os::raw::c_char,
97
639
) -> c_int {
98
639
    if G_DISABLE_HASHING {
99
0
        SCLogError!("SHA1 hashing has been disabled, needed for to_sha1 keyword");
100
0
        return -1;
101
639
    }
102
639
    return SCDetectSignatureAddTransform(s, G_TRANSFORM_SHA1_ID, ptr::null_mut());
103
639
}
104
105
0
fn sha1_transform_do(input: &[u8], output: &mut [u8]) {
106
0
    Sha1::new().chain(input).finalize_into(output.into());
107
0
}
108
109
0
unsafe extern "C" fn sha1_transform(
110
0
    _det: *mut DetectEngineThreadCtx, buffer: *mut InspectionBuffer, _ctx: *mut c_void,
111
0
) {
112
0
    let input = (*buffer).inspect;
113
0
    let input_len = (*buffer).inspect_len;
114
0
    if input.is_null() || input_len == 0 {
115
0
        return;
116
0
    }
117
0
    let input = build_slice!(input, input_len as usize);
118
119
0
    let output = SCInspectionBufferCheckAndExpand(buffer, SC_SHA1_LEN as u32);
120
0
    if output.is_null() {
121
        // allocation failure
122
0
        return;
123
0
    }
124
0
    let output = std::slice::from_raw_parts_mut(output, SC_SHA1_LEN);
125
126
0
    sha1_transform_do(input, output);
127
128
0
    SCInspectionBufferTruncate(buffer, SC_SHA1_LEN as u32);
129
0
}
130
131
#[no_mangle]
132
39
pub unsafe extern "C" fn DetectTransformSha1Register() {
133
39
    let kw = SCTransformTableElmt {
134
39
        name: b"to_sha1\0".as_ptr() as *const libc::c_char,
135
39
        desc: b"convert to sha1 hash of the buffer\0".as_ptr() as *const libc::c_char,
136
39
        url: b"/rules/transforms.html#to-sha1\0".as_ptr() as *const libc::c_char,
137
39
        Setup: Some(sha1_setup),
138
39
        flags: SIGMATCH_NOOPT,
139
39
        Transform: Some(sha1_transform),
140
39
        Free: None,
141
39
        TransformValidate: None,
142
39
        TransformId: None,
143
39
    };
144
39
    G_TRANSFORM_SHA1_ID = SCDetectHelperTransformRegister(&kw);
145
39
    if G_TRANSFORM_SHA1_ID < 0 {
146
0
        SCLogWarning!("Failed registering transform sha1");
147
39
    }
148
39
}
149
150
277
unsafe extern "C" fn sha256_setup(
151
277
    _de: *mut DetectEngineCtx, s: *mut Signature, _raw: *const std::os::raw::c_char,
152
277
) -> c_int {
153
277
    if G_DISABLE_HASHING {
154
0
        SCLogError!("SHA256 hashing has been disabled, needed for to_sha256 keyword");
155
0
        return -1;
156
277
    }
157
277
    return SCDetectSignatureAddTransform(s, G_TRANSFORM_SHA256_ID, ptr::null_mut());
158
277
}
159
160
0
fn sha256_transform_do(input: &[u8], output: &mut [u8]) {
161
0
    Sha256::new().chain(input).finalize_into(output.into());
162
0
}
163
164
0
unsafe extern "C" fn sha256_transform(
165
0
    _det: *mut DetectEngineThreadCtx, buffer: *mut InspectionBuffer, _ctx: *mut c_void,
166
0
) {
167
0
    let input = (*buffer).inspect;
168
0
    let input_len = (*buffer).inspect_len;
169
0
    if input.is_null() || input_len == 0 {
170
0
        return;
171
0
    }
172
0
    let input = build_slice!(input, input_len as usize);
173
174
0
    let output = SCInspectionBufferCheckAndExpand(buffer, SC_SHA256_LEN as u32);
175
0
    if output.is_null() {
176
        // allocation failure
177
0
        return;
178
0
    }
179
0
    let output = std::slice::from_raw_parts_mut(output, SC_SHA256_LEN);
180
181
0
    sha256_transform_do(input, output);
182
183
0
    SCInspectionBufferTruncate(buffer, SC_SHA256_LEN as u32);
184
0
}
185
186
#[no_mangle]
187
39
pub unsafe extern "C" fn DetectTransformSha256Register() {
188
39
    let kw = SCTransformTableElmt {
189
39
        name: b"to_sha256\0".as_ptr() as *const libc::c_char,
190
39
        desc: b"convert to sha256 hash of the buffer\0".as_ptr() as *const libc::c_char,
191
39
        url: b"/rules/transforms.html#to-sha256\0".as_ptr() as *const libc::c_char,
192
39
        Setup: Some(sha256_setup),
193
39
        flags: SIGMATCH_NOOPT,
194
39
        Transform: Some(sha256_transform),
195
39
        Free: None,
196
39
        TransformValidate: None,
197
39
        TransformId: None,
198
39
    };
199
39
    G_TRANSFORM_SHA256_ID = SCDetectHelperTransformRegister(&kw);
200
39
    if G_TRANSFORM_SHA256_ID < 0 {
201
0
        SCLogWarning!("Failed registering transform sha256");
202
39
    }
203
39
}
204
205
#[cfg(test)]
206
mod tests {
207
    use super::*;
208
209
    #[test]
210
    fn test_md5_transform() {
211
        let buf = b" A B C D ";
212
        let mut out = vec![0; SC_MD5_LEN];
213
        md5_transform_do(buf, &mut out);
214
        assert_eq!(
215
            out,
216
            b"\xe0\x59\xf8\x30\x43\x69\x58\xb6\x45\x82\x8c\xc2\x33\xc2\x47\x13"
217
        );
218
    }
219
220
    #[test]
221
    fn test_sha1_transform() {
222
        let buf = b" A B C D ";
223
        let mut out = vec![0; SC_SHA1_LEN];
224
        sha1_transform_do(buf, &mut out);
225
        assert_eq!(
226
            out,
227
            b"\xc8\xdc\x44\x97\xf7\xe0\x55\xf8\x6b\x88\x90\x52\x08\x2c\x0c\x7b\xdc\xc9\xc8\x89"
228
        );
229
    }
230
231
    #[test]
232
    fn test_sha256_transform() {
233
        let mut buf = Vec::with_capacity(SC_SHA256_LEN);
234
        buf.extend_from_slice(b" A B C D ");
235
        let mut out = vec![0; SC_SHA256_LEN];
236
        sha256_transform_do(&buf, &mut out);
237
        assert_eq!(out, b"\xd6\xbf\x7d\x8d\x69\x53\x02\x4d\x0d\x84\x5c\x99\x9b\xae\x93\xcc\xac\x68\xea\xab\x9a\xc9\x77\xd0\xfd\x30\x6a\xf5\x9a\x3d\xe4\x3a");
238
        // test in place
239
        let still_buf = unsafe { std::slice::from_raw_parts(buf.as_ptr(), buf.len()) };
240
        buf.resize(SC_SHA256_LEN, 0);
241
        sha256_transform_do(still_buf, &mut buf);
242
        assert_eq!(&buf, b"\xd6\xbf\x7d\x8d\x69\x53\x02\x4d\x0d\x84\x5c\x99\x9b\xae\x93\xcc\xac\x68\xea\xab\x9a\xc9\x77\xd0\xfd\x30\x6a\xf5\x9a\x3d\xe4\x3a");
243
    }
244
}