suricata
util-lua-hashlib.c
Go to the documentation of this file.
1/* Copyright (C) 2025 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/**
19 * \file
20 *
21 * Hashing library for Lua.
22 *
23 * Usage:
24 *
25 * local hashing = require("suricata.hashing")
26 *
27 * -- One shot hash
28 * hash = hashing.sha256_digest("www.suricata.io")
29 *
30 * -- One shot hash to hex
31 * hash = hashing.sha256_hexdigest("www.suricata.io")
32 *
33 * -- Incremental hashing
34 * hasher = hashing.sha256()
35 * hasher:update("www.")
36 * hasher:update("suricata.io")
37 * hash = hasher:finalize()
38 *
39 * Support hashes: sha256, sha1, md5
40 */
41
42#include "util-lua-hashlib.h"
43
44#include "lauxlib.h"
45#include "rust-bindings.h"
46
47#define SHA256_MT "suricata:hashlib:sha256"
48#define SHA1_MT "suricata:hashlib:sha1"
49#define MD5_MT "suricata:hashlib:md5"
50
51/**
52 * \brief Create a new SHA-256 hash instance.
53 */
54static int LuaHashLibSha256New(lua_State *L)
55{
56 struct SCSha256 **hasher = lua_newuserdata(L, sizeof(struct SCSha256 *));
57 if (hasher == NULL) {
58 return luaL_error(L, "failed to allocate userdata for sha256");
59 }
60 *hasher = SCSha256New();
61 luaL_getmetatable(L, SHA256_MT);
62 lua_setmetatable(L, -2);
63 return 1;
64}
65
66/**
67 * \brief Add more data to an existing SHA-256 hash instance.
68 */
69static int LuaHashLibSha256Update(lua_State *L)
70{
71 struct SCSha256 **hasher = luaL_checkudata(L, 1, SHA256_MT);
72 if (hasher == NULL) {
73 return luaL_error(L, "null userdata");
74 }
75 size_t data_len;
76 const char *data = luaL_checklstring(L, 2, &data_len);
77 SCSha256Update(*hasher, (const uint8_t *)data, (uint32_t)data_len);
78 return 0;
79}
80
81static int LuaHashLibSha256Finalize(lua_State *L)
82{
83 struct SCSha256 **hasher = luaL_checkudata(L, 1, SHA256_MT);
84 if (hasher == NULL) {
85 return luaL_error(L, "null userdata");
86 }
87
88 uint8_t hash[SC_SHA256_LEN];
89 SCSha256Finalize(*hasher, hash, sizeof(hash));
90 lua_pushlstring(L, (const char *)hash, sizeof(hash));
91
92 // Finalize consumes the hasher, so set to NULL so its not free'd
93 // during garbage collection.
94 *hasher = NULL;
95
96 return 1;
97}
98
99static int LuaHashLibSha256FinalizeToHex(lua_State *L)
100{
101 struct SCSha256 **hasher = luaL_checkudata(L, 1, SHA256_MT);
102 if (hasher == NULL) {
103 return luaL_error(L, "null userdata");
104 }
105
106 char hash[SC_SHA256_HEX_LEN + 1];
107 if (!SCSha256FinalizeToHex(*hasher, hash, sizeof(hash))) {
108 *hasher = NULL;
109 return luaL_error(L, "sha256 hashing failed");
110 }
111
112 lua_pushstring(L, (const char *)hash);
113
114 // Finalize consumes the hasher, so set to NULL so its not free'd
115 // during garbage collection.
116 *hasher = NULL;
117
118 return 1;
119}
120
121static int LuaHashLibSha256Digest(lua_State *L)
122{
123 size_t buf_len;
124 const char *input = luaL_checklstring(L, 1, &buf_len);
125
126 uint32_t output_len = SC_SHA256_LEN;
127 uint8_t output[output_len];
128 if (!SCSha256HashBuffer((uint8_t *)input, (uint32_t)buf_len, output, output_len)) {
129 return luaL_error(L, "sha256 hashing failed");
130 }
131
132 lua_pushlstring(L, (const char *)output, output_len);
133
134 return 1;
135}
136
137static int LuaHashLibSha256HexDigest(lua_State *L)
138{
139 size_t buf_len;
140 const char *input = luaL_checklstring(L, 1, &buf_len);
141
142 char output[SC_SHA256_HEX_LEN + 1];
143 if (!SCSha256HashBufferToHex((uint8_t *)input, (uint32_t)buf_len, output, sizeof(output))) {
144 return luaL_error(L, "sha256 hashing failed");
145 }
146
147 lua_pushstring(L, (const char *)output);
148 return 1;
149}
150
151static int LuaHashLibSha256Gc(lua_State *L)
152{
153 struct SCSha256 **hasher = luaL_checkudata(L, 1, SHA256_MT);
154 if (hasher && *hasher) {
155 SCSha256Free(*hasher);
156 }
157 return 0;
158}
159
160static int LuaHashLibSha1New(lua_State *L)
161{
162 struct SCSha1 **hasher = lua_newuserdata(L, sizeof(struct SCSha1 *));
163 if (hasher == NULL) {
164 return luaL_error(L, "failed to allocate userdata for sha1");
165 }
166 *hasher = SCSha1New();
167 luaL_getmetatable(L, SHA1_MT);
168 lua_setmetatable(L, -2);
169 return 1;
170}
171
172static int LuaHashLibSha1Update(lua_State *L)
173{
174 struct SCSha1 **hasher = luaL_checkudata(L, 1, SHA1_MT);
175 if (hasher == NULL) {
176 return luaL_error(L, "null userdata");
177 }
178
179 size_t data_len;
180 const char *data = luaL_checklstring(L, 2, &data_len);
181 SCSha1Update(*hasher, (const uint8_t *)data, (uint32_t)data_len);
182 return 0;
183}
184
185static int LuaHashLibSha1Finalize(lua_State *L)
186{
187 struct SCSha1 **hasher = luaL_checkudata(L, 1, SHA1_MT);
188 if (hasher == NULL) {
189 return luaL_error(L, "null userdata");
190 }
191
192 uint8_t hash[SC_SHA1_LEN];
193 SCSha1Finalize(*hasher, hash, sizeof(hash));
194 lua_pushlstring(L, (const char *)hash, sizeof(hash));
195
196 // Finalize consumes the hasher, so set to NULL so its not free'd
197 // during garbage collection.
198 *hasher = NULL;
199
200 return 1;
201}
202
203static int LuaHashLibSha1FinalizeToHex(lua_State *L)
204{
205 struct SCSha1 **hasher = luaL_checkudata(L, 1, SHA1_MT);
206 if (hasher == NULL) {
207 return luaL_error(L, "null userdata");
208 }
209
210 char hash[SC_SHA1_HEX_LEN + 1];
211 if (!SCSha1FinalizeToHex(*hasher, hash, sizeof(hash))) {
212 *hasher = NULL;
213 return luaL_error(L, "sha1 hashing failed");
214 }
215
216 lua_pushstring(L, (const char *)hash);
217
218 // Finalize consumes the hasher, so set to NULL so its not free'd
219 // during garbage collection.
220 *hasher = NULL;
221
222 return 1;
223}
224
225static int LuaHashLibSha1Digest(lua_State *L)
226{
227 size_t buf_len;
228 const char *input = luaL_checklstring(L, 1, &buf_len);
229
230 uint8_t output[SC_SHA1_LEN];
231 if (!SCSha1HashBuffer((uint8_t *)input, (uint32_t)buf_len, output, sizeof(output))) {
232 return luaL_error(L, "sha1 hashing failed");
233 }
234
235 lua_pushlstring(L, (const char *)output, sizeof(output));
236 return 1;
237}
238
239static int LuaHashLibSha1HexDigest(lua_State *L)
240{
241 size_t buf_len;
242 const char *input = luaL_checklstring(L, 1, &buf_len);
243
244 char output[SC_SHA1_HEX_LEN + 1];
245 if (!SCSha1HashBufferToHex((uint8_t *)input, (uint32_t)buf_len, output, sizeof(output))) {
246 return luaL_error(L, "sha1 hashing failed");
247 }
248
249 lua_pushstring(L, (const char *)output);
250 return 1;
251}
252
253static int LuaHashLibSha1Gc(lua_State *L)
254{
255 struct SCSha1 **hasher = luaL_checkudata(L, 1, SHA1_MT);
256 if (hasher && *hasher) {
257 SCSha1Free(*hasher);
258 }
259 return 0;
260}
261
262static int LuaHashLibMd5New(lua_State *L)
263{
264 struct SCMd5 **hasher = lua_newuserdata(L, sizeof(struct SCMd5 *));
265 if (hasher == NULL) {
266 return luaL_error(L, "failed to allocate userdata for sha1");
267 }
268 *hasher = SCMd5New();
269 luaL_getmetatable(L, MD5_MT);
270 lua_setmetatable(L, -2);
271 return 1;
272}
273
274static int LuaHashLibMd5Update(lua_State *L)
275{
276 struct SCMd5 **hasher = luaL_checkudata(L, 1, MD5_MT);
277 if (hasher == NULL) {
278 return luaL_error(L, "null userdata");
279 }
280
281 size_t data_len;
282 const char *data = luaL_checklstring(L, 2, &data_len);
283 SCMd5Update(*hasher, (const uint8_t *)data, (uint32_t)data_len);
284 return 0;
285}
286
287static int LuaHashLibMd5Finalize(lua_State *L)
288{
289 struct SCMd5 **hasher = luaL_checkudata(L, 1, MD5_MT);
290 if (hasher == NULL) {
291 return luaL_error(L, "null userdata");
292 }
293
294 uint8_t hash[SC_MD5_LEN];
295 SCMd5Finalize(*hasher, hash, sizeof(hash));
296 lua_pushlstring(L, (const char *)hash, sizeof(hash));
297
298 // Finalize consumes the hasher, so set to NULL so its not free'd
299 // during garbage collection.
300 *hasher = NULL;
301
302 return 1;
303}
304
305static int LuaHashLibMd5FinalizeToHex(lua_State *L)
306{
307 struct SCMd5 **hasher = luaL_checkudata(L, 1, MD5_MT);
308 if (hasher == NULL) {
309 return luaL_error(L, "null userdata");
310 }
311
312 char hash[SC_MD5_HEX_LEN + 1];
313 if (!SCMd5FinalizeToHex(*hasher, hash, sizeof(hash))) {
314 *hasher = NULL;
315 return luaL_error(L, "md5 hashing failed");
316 }
317
318 lua_pushstring(L, (const char *)hash);
319
320 // Finalize consumes the hasher, so set to NULL so its not free'd
321 // during garbage collection.
322 *hasher = NULL;
323
324 return 1;
325}
326
327static int LuaHashLibMd5Digest(lua_State *L)
328{
329 size_t buf_len;
330 const char *input = luaL_checklstring(L, 1, &buf_len);
331
332 uint8_t output[SC_MD5_LEN];
333 if (!SCMd5HashBuffer((uint8_t *)input, (uint32_t)buf_len, output, sizeof(output))) {
334 return luaL_error(L, "md5 hashing failed");
335 }
336
337 lua_pushlstring(L, (const char *)output, sizeof(output));
338 return 1;
339}
340
341static int LuaHashLibMd5HexDigest(lua_State *L)
342{
343 size_t buf_len;
344 const char *input = luaL_checklstring(L, 1, &buf_len);
345
346 char output[SC_MD5_HEX_LEN + 1];
347 if (!SCMd5HashBufferToHex((uint8_t *)input, (uint32_t)buf_len, output, sizeof(output))) {
348 return luaL_error(L, "md5 hashing failed");
349 }
350
351 lua_pushstring(L, (const char *)output);
352 return 1;
353}
354
355static int LuaHashLibMd5Gc(lua_State *L)
356{
357 struct SCMd5 **hasher = luaL_checkudata(L, 1, MD5_MT);
358 if (hasher && *hasher) {
359 SCMd5Free(*hasher);
360 }
361 return 0;
362}
363
364static const struct luaL_Reg hashlib[] = {
365 // clang-format off
366 { "sha256_digest", LuaHashLibSha256Digest },
367 { "sha256_hexdigest", LuaHashLibSha256HexDigest },
368 { "sha256", LuaHashLibSha256New },
369 { "sha1_digest", LuaHashLibSha1Digest },
370 { "sha1_hexdigest", LuaHashLibSha1HexDigest },
371 { "sha1", LuaHashLibSha1New },
372 { "md5_digest", LuaHashLibMd5Digest },
373 { "md5_hexdigest", LuaHashLibMd5HexDigest },
374 { "md5", LuaHashLibMd5New },
375 { NULL, NULL },
376 // clang-format on
377};
378
379static const struct luaL_Reg sha256_meta[] = {
380 // clang-format off
381 { "update", LuaHashLibSha256Update },
382 { "finalize", LuaHashLibSha256Finalize },
383 { "finalize_to_hex", LuaHashLibSha256FinalizeToHex },
384 { "__gc", LuaHashLibSha256Gc },
385 { NULL, NULL },
386 // clang-format on
387};
388
389static const struct luaL_Reg sha1_meta[] = {
390 // clang-format off
391 { "update", LuaHashLibSha1Update },
392 { "finalize", LuaHashLibSha1Finalize },
393 { "finalize_to_hex", LuaHashLibSha1FinalizeToHex },
394 { "__gc", LuaHashLibSha1Gc },
395 { NULL, NULL },
396 // clang-format on
397};
398
399static const struct luaL_Reg md5_meta[] = {
400 // clang-format off
401 { "update", LuaHashLibMd5Update },
402 { "finalize", LuaHashLibMd5Finalize },
403 { "finalize_to_hex", LuaHashLibMd5FinalizeToHex },
404 { "__gc", LuaHashLibMd5Gc },
405 { NULL, NULL },
406 // clang-format on
407};
408
410{
411 luaL_newmetatable(L, SHA256_MT);
412 lua_pushvalue(L, -1);
413 lua_setfield(L, -2, "__index");
414 luaL_setfuncs(L, sha256_meta, 0);
415
416 luaL_newmetatable(L, SHA1_MT);
417 lua_pushvalue(L, -1);
418 lua_setfield(L, -2, "__index");
419 luaL_setfuncs(L, sha1_meta, 0);
420
421 luaL_newmetatable(L, MD5_MT);
422 lua_pushvalue(L, -1);
423 lua_setfield(L, -2, "__index");
424 luaL_setfuncs(L, md5_meta, 0);
425
426 luaL_newlib(L, hashlib);
427
428 return 1;
429}
struct lua_State lua_State
struct SCSha1 SCSha1
Definition util-file.h:39
#define SC_SHA1_LEN
Definition util-file.h:40
struct SCSha256 SCSha256
Definition util-file.h:36
struct SCMd5 SCMd5
Definition util-file.h:42
#define SC_MD5_LEN
Definition util-file.h:43
#define SC_SHA256_LEN
Definition util-file.h:37
#define MD5_MT
#define SHA256_MT
#define SHA1_MT
int SCLuaLoadHashlib(lua_State *L)