/src/suricata8/rust/src/ffi/hashing.rs
Line | Count | Source |
1 | | /* Copyright (C) 2020 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 digest::{Digest, Update}; |
19 | | use md5::Md5; |
20 | | use sha1::Sha1; |
21 | | use sha2::Sha256; |
22 | | use std::os::raw::c_char; |
23 | | |
24 | | pub const SC_SHA256_LEN: usize = 32; |
25 | | pub const SC_SHA1_LEN: usize = 20; |
26 | | pub const SC_MD5_LEN: usize = 16; |
27 | | |
28 | | // Length of hex digests without trailing NUL. |
29 | | pub const SC_SHA256_HEX_LEN: usize = 64; |
30 | | pub const SC_SHA1_HEX_LEN: usize = 40; |
31 | | pub const SC_MD5_HEX_LEN: usize = 32; |
32 | | |
33 | | // Wrap the Rust Sha256 in a new type named SCSha256 to give this type |
34 | | // the "SC" prefix. The one drawback is we must access the actual context |
35 | | // with .0. |
36 | | pub struct SCSha256(Sha256); |
37 | | |
38 | | #[no_mangle] |
39 | 980k | pub extern "C" fn SCSha256New() -> *mut SCSha256 { |
40 | 980k | let hasher = Box::new(SCSha256(Sha256::new())); |
41 | 980k | Box::into_raw(hasher) |
42 | 980k | } |
43 | | |
44 | | #[no_mangle] |
45 | 7.29M | pub unsafe extern "C" fn SCSha256Update(hasher: &mut SCSha256, bytes: *const u8, len: u32) { |
46 | 7.29M | update(&mut hasher.0, bytes, len); |
47 | 7.29M | } |
48 | | |
49 | | #[no_mangle] |
50 | 914k | pub unsafe extern "C" fn SCSha256Finalize(hasher: &mut SCSha256, out: *mut u8, len: u32) { |
51 | 914k | let hasher: Box<SCSha256> = Box::from_raw(hasher); |
52 | 914k | finalize(hasher.0, out, len); |
53 | 914k | } |
54 | | |
55 | | /// C function to finalize the Sha256 hasher to a hex string. |
56 | | /// |
57 | | /// Notes: |
58 | | /// - There is probably room for optimization here, by iterating the result and writing |
59 | | /// the output directly to the output buffer. |
60 | | /// |
61 | | /// But even given the notes, this appears to be faster than the equivalent that we |
62 | | /// did in C using NSS. |
63 | | #[no_mangle] |
64 | 0 | pub unsafe extern "C" fn SCSha256FinalizeToHex( |
65 | 0 | hasher: &mut SCSha256, out: *mut c_char, len: u32, |
66 | 0 | ) -> bool { |
67 | 0 | let hasher: Box<SCSha256> = Box::from_raw(hasher); |
68 | 0 | let result = hasher.0.finalize(); |
69 | 0 | let hex = format!("{:x}", &result); |
70 | 0 | crate::ffi::strings::copy_to_c_char(hex, out, len as usize) |
71 | 0 | } |
72 | | |
73 | | /// Free an unfinalized Sha256 context. |
74 | | #[no_mangle] |
75 | 64.3k | pub unsafe extern "C" fn SCSha256Free(hasher: &mut SCSha256) { |
76 | | // Drop. |
77 | 64.3k | let _: Box<SCSha256> = Box::from_raw(hasher); |
78 | 64.3k | } |
79 | | |
80 | | #[no_mangle] |
81 | 0 | pub unsafe extern "C" fn SCSha256HashBuffer( |
82 | 0 | buf: *const u8, buf_len: u32, out: *mut u8, len: u32, |
83 | 0 | ) -> bool { |
84 | 0 | if len as usize != SC_SHA256_LEN { |
85 | 0 | return false; |
86 | 0 | } |
87 | 0 | let data = std::slice::from_raw_parts(buf, buf_len as usize); |
88 | 0 | let output = std::slice::from_raw_parts_mut(out, len as usize); |
89 | 0 | let hash = Sha256::new().chain(data).finalize(); |
90 | 0 | output.copy_from_slice(&hash); |
91 | 0 | return true; |
92 | 0 | } |
93 | | |
94 | | #[no_mangle] |
95 | 0 | pub unsafe extern "C" fn SCSha256HashBufferToHex( |
96 | 0 | buf: *const u8, buf_len: u32, out: *mut c_char, len: u32, |
97 | 0 | ) -> bool { |
98 | 0 | let data = std::slice::from_raw_parts(buf, buf_len as usize); |
99 | 0 | let hash = Sha256::new().chain(data).finalize(); |
100 | 0 | let hex = format!("{:x}", &hash); |
101 | 0 | crate::ffi::strings::copy_to_c_char(hex, out, len as usize) |
102 | 0 | } |
103 | | |
104 | | // Start of SHA1 C bindings. |
105 | | |
106 | | pub struct SCSha1(Sha1); |
107 | | |
108 | | #[no_mangle] |
109 | 977k | pub extern "C" fn SCSha1New() -> *mut SCSha1 { |
110 | 977k | let hasher = Box::new(SCSha1(Sha1::new())); |
111 | 977k | Box::into_raw(hasher) |
112 | 977k | } |
113 | | |
114 | | #[no_mangle] |
115 | 7.23M | pub unsafe extern "C" fn SCSha1Update(hasher: &mut SCSha1, bytes: *const u8, len: u32) { |
116 | 7.23M | update(&mut hasher.0, bytes, len); |
117 | 7.23M | } |
118 | | |
119 | | #[no_mangle] |
120 | 912k | pub unsafe extern "C" fn SCSha1Finalize(hasher: &mut SCSha1, out: *mut u8, len: u32) { |
121 | 912k | let hasher: Box<SCSha1> = Box::from_raw(hasher); |
122 | 912k | finalize(hasher.0, out, len); |
123 | 912k | } |
124 | | |
125 | | #[no_mangle] |
126 | 0 | pub unsafe extern "C" fn SCSha1FinalizeToHex( |
127 | 0 | hasher: &mut SCSha1, out: *mut c_char, len: u32, |
128 | 0 | ) -> bool { |
129 | 0 | let hasher: Box<SCSha1> = Box::from_raw(hasher); |
130 | 0 | let result = hasher.0.finalize(); |
131 | 0 | let hex = format!("{:x}", &result); |
132 | 0 | crate::ffi::strings::copy_to_c_char(hex, out, len as usize) |
133 | 0 | } |
134 | | |
135 | | /// Free an unfinalized Sha1 context. |
136 | | #[no_mangle] |
137 | 63.7k | pub unsafe extern "C" fn SCSha1Free(hasher: &mut SCSha1) { |
138 | | // Drop. |
139 | 63.7k | let _: Box<SCSha1> = Box::from_raw(hasher); |
140 | 63.7k | } |
141 | | |
142 | | #[no_mangle] |
143 | 0 | pub unsafe extern "C" fn SCSha1HashBuffer( |
144 | 0 | buf: *const u8, buf_len: u32, out: *mut u8, len: u32, |
145 | 0 | ) -> bool { |
146 | 0 | if len as usize != SC_SHA1_LEN { |
147 | 0 | return false; |
148 | 0 | } |
149 | 0 | let data = std::slice::from_raw_parts(buf, buf_len as usize); |
150 | 0 | let output = std::slice::from_raw_parts_mut(out, len as usize); |
151 | 0 | let hash = Sha1::new().chain(data).finalize(); |
152 | 0 | output.copy_from_slice(&hash); |
153 | 0 | return true; |
154 | 0 | } |
155 | | |
156 | | #[no_mangle] |
157 | 0 | pub unsafe extern "C" fn SCSha1HashBufferToHex( |
158 | 0 | buf: *const u8, buf_len: u32, out: *mut c_char, len: u32, |
159 | 0 | ) -> bool { |
160 | 0 | let data = std::slice::from_raw_parts(buf, buf_len as usize); |
161 | 0 | let hash = Sha1::new().chain(data).finalize(); |
162 | 0 | let hex = format!("{:x}", &hash); |
163 | 0 | crate::ffi::strings::copy_to_c_char(hex, out, len as usize) |
164 | 0 | } |
165 | | |
166 | | // Start of MD5 C bindings. |
167 | | |
168 | | pub struct SCMd5(Md5); |
169 | | |
170 | | #[no_mangle] |
171 | 977k | pub extern "C" fn SCMd5New() -> *mut SCMd5 { |
172 | 977k | let hasher = Box::new(SCMd5(Md5::new())); |
173 | 977k | Box::into_raw(hasher) |
174 | 977k | } |
175 | | |
176 | | #[no_mangle] |
177 | 7.23M | pub unsafe extern "C" fn SCMd5Update(hasher: &mut SCMd5, bytes: *const u8, len: u32) { |
178 | 7.23M | update(&mut hasher.0, bytes, len); |
179 | 7.23M | } |
180 | | |
181 | | /// Finalize the MD5 hash placing the digest in the provided out buffer. |
182 | | /// |
183 | | /// This function consumes the SCMd5 hash context. |
184 | | #[no_mangle] |
185 | 912k | pub unsafe extern "C" fn SCMd5Finalize(hasher: &mut SCMd5, out: *mut u8, len: u32) { |
186 | 912k | let hasher: Box<SCMd5> = Box::from_raw(hasher); |
187 | 912k | finalize(hasher.0, out, len); |
188 | 912k | } |
189 | | |
190 | | /// Finalize MD5 context to a hex string. |
191 | | /// |
192 | | /// Consumes the hash context and cannot be re-used. |
193 | | #[no_mangle] |
194 | 0 | pub unsafe extern "C" fn SCMd5FinalizeToHex( |
195 | 0 | hasher: &mut SCMd5, out: *mut c_char, len: u32, |
196 | 0 | ) -> bool { |
197 | 0 | let hasher: Box<SCMd5> = Box::from_raw(hasher); |
198 | 0 | let result = hasher.0.finalize(); |
199 | 0 | let hex = format!("{:x}", &result); |
200 | 0 | crate::ffi::strings::copy_to_c_char(hex, out, len as usize) |
201 | 0 | } |
202 | | |
203 | | /// Free an unfinalized Sha1 context. |
204 | | #[no_mangle] |
205 | 63.7k | pub unsafe extern "C" fn SCMd5Free(hasher: &mut SCMd5) { |
206 | | // Drop. |
207 | 63.7k | let _: Box<SCMd5> = Box::from_raw(hasher); |
208 | 63.7k | } |
209 | | |
210 | | #[no_mangle] |
211 | 0 | pub unsafe extern "C" fn SCMd5HashBuffer( |
212 | 0 | buf: *const u8, buf_len: u32, out: *mut u8, len: u32, |
213 | 0 | ) -> bool { |
214 | 0 | if len as usize != SC_MD5_LEN { |
215 | 0 | return false; |
216 | 0 | } |
217 | 0 | let data = std::slice::from_raw_parts(buf, buf_len as usize); |
218 | 0 | let output = std::slice::from_raw_parts_mut(out, len as usize); |
219 | 0 | let hash = Md5::new().chain(data).finalize(); |
220 | 0 | output.copy_from_slice(&hash); |
221 | 0 | true |
222 | 0 | } |
223 | | |
224 | | /// C binding for a function to MD5 hash a single buffer to a hex string. |
225 | | #[no_mangle] |
226 | 89 | pub unsafe extern "C" fn SCMd5HashBufferToHex( |
227 | 89 | buf: *const u8, buf_len: u32, out: *mut c_char, len: u32, |
228 | 89 | ) -> bool { |
229 | 89 | let data = std::slice::from_raw_parts(buf, buf_len as usize); |
230 | 89 | let hash = Md5::new().chain(data).finalize(); |
231 | 89 | let hex = format!("{:x}", &hash); |
232 | 89 | crate::ffi::strings::copy_to_c_char(hex, out, len as usize) |
233 | 89 | } |
234 | | |
235 | | // Functions that are generic over Digest. For the most part the C bindings are |
236 | | // just wrappers around these. |
237 | | |
238 | 21.7M | unsafe fn update<D: Digest>(digest: &mut D, bytes: *const u8, len: u32) { |
239 | 21.7M | let data = std::slice::from_raw_parts(bytes, len as usize); |
240 | 21.7M | digest.update(data); |
241 | 21.7M | } suricata::ffi::hashing::update::<digest::core_api::wrapper::CoreWrapper<digest::core_api::ct_variable::CtVariableCoreWrapper<sha2::core_api::Sha256VarCore, typenum::uint::UInt<typenum::uint::UInt<typenum::uint::UInt<typenum::uint::UInt<typenum::uint::UInt<typenum::uint::UInt<typenum::uint::UTerm, typenum::bit::B1>, typenum::bit::B0>, typenum::bit::B0>, typenum::bit::B0>, typenum::bit::B0>, typenum::bit::B0>, sha2::OidSha256>>> Line | Count | Source | 238 | 7.29M | unsafe fn update<D: Digest>(digest: &mut D, bytes: *const u8, len: u32) { | 239 | 7.29M | let data = std::slice::from_raw_parts(bytes, len as usize); | 240 | 7.29M | digest.update(data); | 241 | 7.29M | } |
suricata::ffi::hashing::update::<digest::core_api::wrapper::CoreWrapper<md5::Md5Core>> Line | Count | Source | 238 | 7.23M | unsafe fn update<D: Digest>(digest: &mut D, bytes: *const u8, len: u32) { | 239 | 7.23M | let data = std::slice::from_raw_parts(bytes, len as usize); | 240 | 7.23M | digest.update(data); | 241 | 7.23M | } |
suricata::ffi::hashing::update::<digest::core_api::wrapper::CoreWrapper<sha1::Sha1Core>> Line | Count | Source | 238 | 7.23M | unsafe fn update<D: Digest>(digest: &mut D, bytes: *const u8, len: u32) { | 239 | 7.23M | let data = std::slice::from_raw_parts(bytes, len as usize); | 240 | 7.23M | digest.update(data); | 241 | 7.23M | } |
|
242 | | |
243 | 2.73M | unsafe fn finalize<D: Digest>(digest: D, out: *mut u8, len: u32) { |
244 | 2.73M | let result = digest.finalize(); |
245 | 2.73M | let output = std::slice::from_raw_parts_mut(out, len as usize); |
246 | | // This will panic if the sizes differ. |
247 | 2.73M | output.copy_from_slice(&result); |
248 | 2.73M | } suricata::ffi::hashing::finalize::<digest::core_api::wrapper::CoreWrapper<digest::core_api::ct_variable::CtVariableCoreWrapper<sha2::core_api::Sha256VarCore, typenum::uint::UInt<typenum::uint::UInt<typenum::uint::UInt<typenum::uint::UInt<typenum::uint::UInt<typenum::uint::UInt<typenum::uint::UTerm, typenum::bit::B1>, typenum::bit::B0>, typenum::bit::B0>, typenum::bit::B0>, typenum::bit::B0>, typenum::bit::B0>, sha2::OidSha256>>> Line | Count | Source | 243 | 914k | unsafe fn finalize<D: Digest>(digest: D, out: *mut u8, len: u32) { | 244 | 914k | let result = digest.finalize(); | 245 | 914k | let output = std::slice::from_raw_parts_mut(out, len as usize); | 246 | | // This will panic if the sizes differ. | 247 | 914k | output.copy_from_slice(&result); | 248 | 914k | } |
suricata::ffi::hashing::finalize::<digest::core_api::wrapper::CoreWrapper<md5::Md5Core>> Line | Count | Source | 243 | 912k | unsafe fn finalize<D: Digest>(digest: D, out: *mut u8, len: u32) { | 244 | 912k | let result = digest.finalize(); | 245 | 912k | let output = std::slice::from_raw_parts_mut(out, len as usize); | 246 | | // This will panic if the sizes differ. | 247 | 912k | output.copy_from_slice(&result); | 248 | 912k | } |
suricata::ffi::hashing::finalize::<digest::core_api::wrapper::CoreWrapper<sha1::Sha1Core>> Line | Count | Source | 243 | 912k | unsafe fn finalize<D: Digest>(digest: D, out: *mut u8, len: u32) { | 244 | 912k | let result = digest.finalize(); | 245 | 912k | let output = std::slice::from_raw_parts_mut(out, len as usize); | 246 | | // This will panic if the sizes differ. | 247 | 912k | output.copy_from_slice(&result); | 248 | 912k | } |
|
249 | | |
250 | | pub static mut G_DISABLE_HASHING: bool = false; |
251 | | |
252 | | #[no_mangle] |
253 | 0 | pub unsafe extern "C" fn SCDisableHashing() { |
254 | 0 | G_DISABLE_HASHING = true; |
255 | 0 | } |
256 | | |
257 | | #[cfg(test)] |
258 | | mod test { |
259 | | use super::*; |
260 | | |
261 | | // A test around SCSha256 primarily to check that the output is |
262 | | // correctly copied into a C string. |
263 | | #[test] |
264 | | fn test_sha256() { |
265 | | unsafe { |
266 | | let hasher = SCSha256New(); |
267 | | assert!(!hasher.is_null()); |
268 | | let hasher = &mut *hasher as &mut SCSha256; |
269 | | let bytes = &[0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41]; |
270 | | SCSha256Update(hasher, bytes.as_ptr(), bytes.len() as u32); |
271 | | SCSha256Update(hasher, bytes.as_ptr(), bytes.len() as u32); |
272 | | SCSha256Update(hasher, bytes.as_ptr(), bytes.len() as u32); |
273 | | SCSha256Update(hasher, bytes.as_ptr(), bytes.len() as u32); |
274 | | let hex = [0_u8; SC_SHA256_HEX_LEN + 1]; |
275 | | SCSha256FinalizeToHex( |
276 | | hasher, |
277 | | hex.as_ptr() as *mut c_char, |
278 | | (SC_SHA256_HEX_LEN + 1) as u32, |
279 | | ); |
280 | | let string = std::ffi::CStr::from_ptr(hex.as_ptr() as *mut c_char) |
281 | | .to_str() |
282 | | .unwrap(); |
283 | | assert_eq!( |
284 | | string, |
285 | | "22a48051594c1949deed7040850c1f0f8764537f5191be56732d16a54c1d8153" |
286 | | ); |
287 | | } |
288 | | } |
289 | | |
290 | | // A test around SCSha256 primarily to check that the output is |
291 | | // correctly copied into a C string. |
292 | | #[test] |
293 | | fn test_md5() { |
294 | | unsafe { |
295 | | let hasher = SCMd5New(); |
296 | | assert!(!hasher.is_null()); |
297 | | let hasher = &mut *hasher as &mut SCMd5; |
298 | | let bytes = &[0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41]; |
299 | | SCMd5Update(hasher, bytes.as_ptr(), bytes.len() as u32); |
300 | | SCMd5Update(hasher, bytes.as_ptr(), bytes.len() as u32); |
301 | | SCMd5Update(hasher, bytes.as_ptr(), bytes.len() as u32); |
302 | | SCMd5Update(hasher, bytes.as_ptr(), bytes.len() as u32); |
303 | | let hex = [0_u8; SC_MD5_HEX_LEN + 1]; |
304 | | SCMd5FinalizeToHex( |
305 | | hasher, |
306 | | hex.as_ptr() as *mut c_char, |
307 | | (SC_MD5_HEX_LEN + 1) as u32, |
308 | | ); |
309 | | let string = std::ffi::CStr::from_ptr(hex.as_ptr() as *mut c_char) |
310 | | .to_str() |
311 | | .unwrap(); |
312 | | assert_eq!(string, "5216ddcc58e8dade5256075e77f642da"); |
313 | | } |
314 | | } |
315 | | } |