/src/suricata7/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_SHA1_LEN: usize = 20; |
25 | | pub const SC_SHA256_LEN: usize = 32; |
26 | | |
27 | | // Length of hex digests without trailing NUL. |
28 | | pub const SC_MD5_HEX_LEN: usize = 32; |
29 | | pub const SC_SHA256_HEX_LEN: usize = 64; |
30 | | |
31 | | // Wrap the Rust Sha256 in a new type named SCSha256 to give this type |
32 | | // the "SC" prefix. The one drawback is we must access the actual context |
33 | | // with .0. |
34 | | pub struct SCSha256(Sha256); |
35 | | |
36 | | #[no_mangle] |
37 | 587k | pub extern "C" fn SCSha256New() -> *mut SCSha256 { |
38 | 587k | let hasher = Box::new(SCSha256(Sha256::new())); |
39 | 587k | Box::into_raw(hasher) |
40 | 587k | } |
41 | | |
42 | | #[no_mangle] |
43 | 5.87M | pub unsafe extern "C" fn SCSha256Update(hasher: &mut SCSha256, bytes: *const u8, len: u32) { |
44 | 5.87M | update(&mut hasher.0, bytes, len); |
45 | 5.87M | } |
46 | | |
47 | | #[no_mangle] |
48 | 473k | pub unsafe extern "C" fn SCSha256Finalize(hasher: &mut SCSha256, out: *mut u8, len: u32) { |
49 | 473k | let hasher: Box<SCSha256> = Box::from_raw(hasher); |
50 | 473k | finalize(hasher.0, out, len); |
51 | 473k | } |
52 | | |
53 | | /// C function to finalize the Sha256 hasher to a hex string. |
54 | | /// |
55 | | /// Notes: |
56 | | /// - There is probably room for optimization here, by iterating the result and writing |
57 | | /// the output directly to the output buffer. |
58 | | /// |
59 | | /// But even given the notes, this appears to be faster than the equivalent that we |
60 | | /// did in C using NSS. |
61 | | #[no_mangle] |
62 | 0 | pub unsafe extern "C" fn SCSha256FinalizeToHex(hasher: &mut SCSha256, out: *mut c_char, len: u32) { |
63 | 0 | let hasher: Box<SCSha256> = Box::from_raw(hasher); |
64 | 0 | let result = hasher.0.finalize(); |
65 | 0 | let hex = format!("{:x}", &result); |
66 | 0 | crate::ffi::strings::copy_to_c_char(hex, out, len as usize); |
67 | 0 | } |
68 | | |
69 | | /// Free an unfinalized Sha256 context. |
70 | | #[no_mangle] |
71 | 113k | pub unsafe extern "C" fn SCSha256Free(hasher: &mut SCSha256) { |
72 | | // Drop. |
73 | 113k | let _: Box<SCSha256> = Box::from_raw(hasher); |
74 | 113k | } |
75 | | |
76 | | #[no_mangle] |
77 | 10 | pub unsafe extern "C" fn SCSha256HashBuffer( |
78 | 10 | buf: *const u8, buf_len: u32, out: *mut u8, len: u32, |
79 | 10 | ) -> bool { |
80 | 10 | if len as usize != SC_SHA256_LEN { |
81 | 0 | return false; |
82 | 10 | } |
83 | 10 | let data = std::slice::from_raw_parts(buf, buf_len as usize); |
84 | 10 | let output = std::slice::from_raw_parts_mut(out, len as usize); |
85 | 10 | let hash = Sha256::new().chain(data).finalize(); |
86 | 10 | output.copy_from_slice(&hash); |
87 | 10 | return true; |
88 | 10 | } |
89 | | |
90 | | // Start of SHA1 C bindings. |
91 | | |
92 | | pub struct SCSha1(Sha1); |
93 | | |
94 | | #[no_mangle] |
95 | 515k | pub extern "C" fn SCSha1New() -> *mut SCSha1 { |
96 | 515k | let hasher = Box::new(SCSha1(Sha1::new())); |
97 | 515k | Box::into_raw(hasher) |
98 | 515k | } |
99 | | |
100 | | #[no_mangle] |
101 | 5.34M | pub unsafe extern "C" fn SCSha1Update(hasher: &mut SCSha1, bytes: *const u8, len: u32) { |
102 | 5.34M | update(&mut hasher.0, bytes, len); |
103 | 5.34M | } |
104 | | |
105 | | #[no_mangle] |
106 | 405k | pub unsafe extern "C" fn SCSha1Finalize(hasher: &mut SCSha1, out: *mut u8, len: u32) { |
107 | 405k | let hasher: Box<SCSha1> = Box::from_raw(hasher); |
108 | 405k | finalize(hasher.0, out, len); |
109 | 405k | } |
110 | | |
111 | | /// Free an unfinalized Sha1 context. |
112 | | #[no_mangle] |
113 | 109k | pub unsafe extern "C" fn SCSha1Free(hasher: &mut SCSha1) { |
114 | | // Drop. |
115 | 109k | let _: Box<SCSha1> = Box::from_raw(hasher); |
116 | 109k | } |
117 | | |
118 | | #[no_mangle] |
119 | 2.29k | pub unsafe extern "C" fn SCSha1HashBuffer( |
120 | 2.29k | buf: *const u8, buf_len: u32, out: *mut u8, len: u32, |
121 | 2.29k | ) -> bool { |
122 | 2.29k | if len as usize != SC_SHA1_LEN { |
123 | 0 | return false; |
124 | 2.29k | } |
125 | 2.29k | let data = std::slice::from_raw_parts(buf, buf_len as usize); |
126 | 2.29k | let output = std::slice::from_raw_parts_mut(out, len as usize); |
127 | 2.29k | let hash = Sha1::new().chain(data).finalize(); |
128 | 2.29k | output.copy_from_slice(&hash); |
129 | 2.29k | return true; |
130 | 2.29k | } |
131 | | |
132 | | // Start of MD5 C bindings. |
133 | | |
134 | | pub struct SCMd5(Md5); |
135 | | |
136 | | #[no_mangle] |
137 | 515k | pub extern "C" fn SCMd5New() -> *mut SCMd5 { |
138 | 515k | let hasher = Box::new(SCMd5(Md5::new())); |
139 | 515k | Box::into_raw(hasher) |
140 | 515k | } |
141 | | |
142 | | #[no_mangle] |
143 | 5.34M | pub unsafe extern "C" fn SCMd5Update(hasher: &mut SCMd5, bytes: *const u8, len: u32) { |
144 | 5.34M | update(&mut hasher.0, bytes, len); |
145 | 5.34M | } |
146 | | |
147 | | /// Finalize the MD5 hash placing the digest in the provided out buffer. |
148 | | /// |
149 | | /// This function consumes the SCMd5 hash context. |
150 | | #[no_mangle] |
151 | 405k | pub unsafe extern "C" fn SCMd5Finalize(hasher: &mut SCMd5, out: *mut u8, len: u32) { |
152 | 405k | let hasher: Box<SCMd5> = Box::from_raw(hasher); |
153 | 405k | finalize(hasher.0, out, len); |
154 | 405k | } |
155 | | |
156 | | /// Finalize MD5 context to a hex string. |
157 | | /// |
158 | | /// Consumes the hash context and cannot be re-used. |
159 | | #[no_mangle] |
160 | 0 | pub unsafe extern "C" fn SCMd5FinalizeToHex(hasher: &mut SCMd5, out: *mut c_char, len: u32) { |
161 | 0 | let hasher: Box<SCMd5> = Box::from_raw(hasher); |
162 | 0 | let result = hasher.0.finalize(); |
163 | 0 | let hex = format!("{:x}", &result); |
164 | 0 | crate::ffi::strings::copy_to_c_char(hex, out, len as usize); |
165 | 0 | } |
166 | | |
167 | | /// Free an unfinalized Sha1 context. |
168 | | #[no_mangle] |
169 | 109k | pub unsafe extern "C" fn SCMd5Free(hasher: &mut SCMd5) { |
170 | | // Drop. |
171 | 109k | let _: Box<SCMd5> = Box::from_raw(hasher); |
172 | 109k | } |
173 | | |
174 | | #[no_mangle] |
175 | 88 | pub unsafe extern "C" fn SCMd5HashBuffer(buf: *const u8, buf_len: u32, out: *mut u8, len: u32) { |
176 | 88 | let data = std::slice::from_raw_parts(buf, buf_len as usize); |
177 | 88 | let output = std::slice::from_raw_parts_mut(out, len as usize); |
178 | 88 | let hash = Md5::new().chain(data).finalize(); |
179 | 88 | output.copy_from_slice(&hash); |
180 | 88 | } |
181 | | |
182 | | /// C binding for a function to MD5 hash a single buffer to a hex string. |
183 | | #[no_mangle] |
184 | 10.1k | pub unsafe extern "C" fn SCMd5HashBufferToHex( |
185 | 10.1k | buf: *const u8, buf_len: u32, out: *mut c_char, len: u32, |
186 | 10.1k | ) { |
187 | 10.1k | let data = std::slice::from_raw_parts(buf, buf_len as usize); |
188 | 10.1k | let hash = Md5::new().chain(data).finalize(); |
189 | 10.1k | let hex = format!("{:x}", &hash); |
190 | 10.1k | crate::ffi::strings::copy_to_c_char(hex, out, len as usize); |
191 | 10.1k | } |
192 | | |
193 | | // Functions that are generic over Digest. For the most part the C bindings are |
194 | | // just wrappers around these. |
195 | | |
196 | 16.5M | unsafe fn update<D: Digest>(digest: &mut D, bytes: *const u8, len: u32) { |
197 | 16.5M | let data = std::slice::from_raw_parts(bytes, len as usize); |
198 | 16.5M | digest.update(data); |
199 | 16.5M | } 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 | 196 | 5.87M | unsafe fn update<D: Digest>(digest: &mut D, bytes: *const u8, len: u32) { | 197 | 5.87M | let data = std::slice::from_raw_parts(bytes, len as usize); | 198 | 5.87M | digest.update(data); | 199 | 5.87M | } |
suricata::ffi::hashing::update::<digest::core_api::wrapper::CoreWrapper<sha1::Sha1Core>> Line | Count | Source | 196 | 5.34M | unsafe fn update<D: Digest>(digest: &mut D, bytes: *const u8, len: u32) { | 197 | 5.34M | let data = std::slice::from_raw_parts(bytes, len as usize); | 198 | 5.34M | digest.update(data); | 199 | 5.34M | } |
suricata::ffi::hashing::update::<digest::core_api::wrapper::CoreWrapper<md5::Md5Core>> Line | Count | Source | 196 | 5.34M | unsafe fn update<D: Digest>(digest: &mut D, bytes: *const u8, len: u32) { | 197 | 5.34M | let data = std::slice::from_raw_parts(bytes, len as usize); | 198 | 5.34M | digest.update(data); | 199 | 5.34M | } |
|
200 | | |
201 | 1.28M | unsafe fn finalize<D: Digest>(digest: D, out: *mut u8, len: u32) { |
202 | 1.28M | let result = digest.finalize(); |
203 | 1.28M | let output = std::slice::from_raw_parts_mut(out, len as usize); |
204 | | // This will panic if the sizes differ. |
205 | 1.28M | output.copy_from_slice(&result); |
206 | 1.28M | } 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 | 201 | 473k | unsafe fn finalize<D: Digest>(digest: D, out: *mut u8, len: u32) { | 202 | 473k | let result = digest.finalize(); | 203 | 473k | let output = std::slice::from_raw_parts_mut(out, len as usize); | 204 | | // This will panic if the sizes differ. | 205 | 473k | output.copy_from_slice(&result); | 206 | 473k | } |
suricata::ffi::hashing::finalize::<digest::core_api::wrapper::CoreWrapper<sha1::Sha1Core>> Line | Count | Source | 201 | 405k | unsafe fn finalize<D: Digest>(digest: D, out: *mut u8, len: u32) { | 202 | 405k | let result = digest.finalize(); | 203 | 405k | let output = std::slice::from_raw_parts_mut(out, len as usize); | 204 | | // This will panic if the sizes differ. | 205 | 405k | output.copy_from_slice(&result); | 206 | 405k | } |
suricata::ffi::hashing::finalize::<digest::core_api::wrapper::CoreWrapper<md5::Md5Core>> Line | Count | Source | 201 | 405k | unsafe fn finalize<D: Digest>(digest: D, out: *mut u8, len: u32) { | 202 | 405k | let result = digest.finalize(); | 203 | 405k | let output = std::slice::from_raw_parts_mut(out, len as usize); | 204 | | // This will panic if the sizes differ. | 205 | 405k | output.copy_from_slice(&result); | 206 | 405k | } |
|
207 | | |
208 | | #[cfg(test)] |
209 | | mod test { |
210 | | use super::*; |
211 | | |
212 | | // A test around SCSha256 primarily to check that the output is |
213 | | // correctly copied into a C string. |
214 | | #[test] |
215 | | fn test_sha256() { |
216 | | unsafe { |
217 | | let hasher = SCSha256New(); |
218 | | assert!(!hasher.is_null()); |
219 | | let hasher = &mut *hasher as &mut SCSha256; |
220 | | let bytes = &[0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41]; |
221 | | SCSha256Update(hasher, bytes.as_ptr(), bytes.len() as u32); |
222 | | SCSha256Update(hasher, bytes.as_ptr(), bytes.len() as u32); |
223 | | SCSha256Update(hasher, bytes.as_ptr(), bytes.len() as u32); |
224 | | SCSha256Update(hasher, bytes.as_ptr(), bytes.len() as u32); |
225 | | let hex = [0_u8; SC_SHA256_HEX_LEN + 1]; |
226 | | SCSha256FinalizeToHex(hasher, hex.as_ptr() as *mut c_char, (SC_SHA256_HEX_LEN + 1) as u32); |
227 | | let string = std::ffi::CStr::from_ptr(hex.as_ptr() as *mut c_char).to_str().unwrap(); |
228 | | assert_eq!(string, "22a48051594c1949deed7040850c1f0f8764537f5191be56732d16a54c1d8153"); |
229 | | } |
230 | | } |
231 | | |
232 | | // A test around SCSha256 primarily to check that the output is |
233 | | // correctly copied into a C string. |
234 | | #[test] |
235 | | fn test_md5() { |
236 | | unsafe { |
237 | | let hasher = SCMd5New(); |
238 | | assert!(!hasher.is_null()); |
239 | | let hasher = &mut *hasher as &mut SCMd5; |
240 | | let bytes = &[0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41]; |
241 | | SCMd5Update(hasher, bytes.as_ptr(), bytes.len() as u32); |
242 | | SCMd5Update(hasher, bytes.as_ptr(), bytes.len() as u32); |
243 | | SCMd5Update(hasher, bytes.as_ptr(), bytes.len() as u32); |
244 | | SCMd5Update(hasher, bytes.as_ptr(), bytes.len() as u32); |
245 | | let hex = [0_u8; SC_MD5_HEX_LEN + 1]; |
246 | | SCMd5FinalizeToHex(hasher, hex.as_ptr() as *mut c_char, (SC_MD5_HEX_LEN + 1) as u32); |
247 | | let string = std::ffi::CStr::from_ptr(hex.as_ptr() as *mut c_char).to_str().unwrap(); |
248 | | assert_eq!(string, "5216ddcc58e8dade5256075e77f642da"); |
249 | | } |
250 | | } |
251 | | |
252 | | } |