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 "suricata-common.h"
43 #include "suricata.h"
44 #include "util-lua-hashlib.h"
45 
46 #include "lauxlib.h"
47 #include "rust-bindings.h"
48 #include "util-lua-common.h"
49 
50 #define SHA256_MT "suricata:hashlib:sha256"
51 
52 /* --disable-hashing is enforced by the transforms, file hashing and
53  * JA3/JA4: the Lua API honors it as well */
54 static int CheckHashingEnabled(lua_State *L)
55 {
56  if (g_disable_hashing) {
57  return luaL_error(L, "hashing is disabled");
58  }
59  return 0;
60 }
61 
62 /* the FFI takes a uint32_t length: reject inputs that would truncate */
63 static int CheckInputLength(lua_State *L, size_t len)
64 {
65  if (len > (size_t)UINT32_MAX) {
66  return luaL_error(L, "input too long: %I bytes", (lua_Integer)len);
67  }
68  return 0;
69 }
70 #define SHA1_MT "suricata:hashlib:sha1"
71 #define MD5_MT "suricata:hashlib:md5"
72 
73 /**
74  * \brief Create a new SHA-256 hash instance.
75  */
76 static int LuaHashLibSha256New(lua_State *L)
77 {
78  if (CheckHashingEnabled(L)) {
79  return 1;
80  }
81  struct SCSha256 **hasher = SCLuaNewUserData(L, SCLUA_TUD_SHA256, sizeof(struct SCSha256 *));
82  if (hasher == NULL) {
83  return luaL_error(L, "failed to allocate userdata for sha256");
84  }
85  *hasher = SCSha256New();
86  luaL_getmetatable(L, SHA256_MT);
87  lua_setmetatable(L, -2);
88  return 1;
89 }
90 
91 /**
92  * \brief Add more data to an existing SHA-256 hash instance.
93  */
94 static int LuaHashLibSha256Update(lua_State *L)
95 {
96  struct SCSha256 **hasher = SCLuaCheckUserData(L, 1, SHA256_MT, SCLUA_TUD_SHA256);
97  if (hasher == NULL || *hasher == NULL) {
98  return luaL_error(L, "invalid sha256 hash context");
99  }
100  if (*hasher == NULL) {
101  return luaL_error(L, "sha256 hasher already finalized");
102  }
103  size_t data_len;
104  const char *data = luaL_checklstring(L, 2, &data_len);
105  if (CheckInputLength(L, data_len)) {
106  return 1;
107  }
108  SCSha256Update(*hasher, (const uint8_t *)data, (uint32_t)data_len);
109  return 0;
110 }
111 
112 static int LuaHashLibSha256Finalize(lua_State *L)
113 {
114  struct SCSha256 **hasher = SCLuaCheckUserData(L, 1, SHA256_MT, SCLUA_TUD_SHA256);
115  if (hasher == NULL || *hasher == NULL) {
116  return luaL_error(L, "invalid sha256 hash context");
117  }
118  if (*hasher == NULL) {
119  return luaL_error(L, "sha256 hasher already finalized");
120  }
121 
122  uint8_t hash[SC_SHA256_LEN];
123  struct SCSha256 *ctx = *hasher;
124  *hasher = NULL;
125  SCSha256Finalize(ctx, hash, sizeof(hash));
126  lua_pushlstring(L, (const char *)hash, sizeof(hash));
127 
128  return 1;
129 }
130 
131 static int LuaHashLibSha256FinalizeToHex(lua_State *L)
132 {
133  struct SCSha256 **hasher = SCLuaCheckUserData(L, 1, SHA256_MT, SCLUA_TUD_SHA256);
134  if (hasher == NULL || *hasher == NULL) {
135  return luaL_error(L, "invalid sha256 hash context");
136  }
137  if (*hasher == NULL) {
138  return luaL_error(L, "sha256 hasher already finalized");
139  }
140 
141  char hash[SC_SHA256_HEX_LEN + 1];
142  struct SCSha256 *ctx = *hasher;
143  *hasher = NULL;
144  if (!SCSha256FinalizeToHex(ctx, hash, sizeof(hash))) {
145  return luaL_error(L, "sha256 hashing failed");
146  }
147 
148  lua_pushstring(L, (const char *)hash);
149 
150  return 1;
151 }
152 
153 static int LuaHashLibSha256Digest(lua_State *L)
154 {
155  size_t buf_len;
156  const char *input = luaL_checklstring(L, 1, &buf_len);
157 
158  if (CheckHashingEnabled(L)) {
159  return 1;
160  }
161  if (CheckInputLength(L, buf_len)) {
162  return 1;
163  }
164 
165  uint8_t output[SC_SHA256_LEN];
166  if (!SCSha256HashBuffer((uint8_t *)input, (uint32_t)buf_len, output, SC_SHA256_LEN)) {
167  return luaL_error(L, "sha256 hashing failed");
168  }
169 
170  lua_pushlstring(L, (const char *)output, SC_SHA256_LEN);
171 
172  return 1;
173 }
174 
175 static int LuaHashLibSha256HexDigest(lua_State *L)
176 {
177  size_t buf_len;
178  const char *input = luaL_checklstring(L, 1, &buf_len);
179 
180  if (CheckHashingEnabled(L)) {
181  return 1;
182  }
183  if (CheckInputLength(L, buf_len)) {
184  return 1;
185  }
186 
187  char output[SC_SHA256_HEX_LEN + 1];
188  if (!SCSha256HashBufferToHex((uint8_t *)input, (uint32_t)buf_len, output, sizeof(output))) {
189  return luaL_error(L, "sha256 hashing failed");
190  }
191 
192  lua_pushstring(L, (const char *)output);
193  return 1;
194 }
195 
196 static int LuaHashLibGc(lua_State *L)
197 {
198  /* free by the payload's own tag, not the metatable's: a script can
199  * re-metatable a hash handle (debug.setmetatable) and then a
200  * different hasher's __gc runs; a metatable-only check would never
201  * free the payload (ASan leak) */
202  if (lua_type(L, 1) != LUA_TUSERDATA) {
203  return 0;
204  }
205  SCLuaUserDataHeader *h = (SCLuaUserDataHeader *)lua_touserdata(L, 1);
206  if (h == NULL || h->magic != SCLUA_UD_MAGIC) {
207  return 0;
208  }
209  uint8_t *payload = (uint8_t *)h + sizeof(*h);
210  switch (h->type_id) {
211  case SCLUA_TUD_SHA256: {
212  struct SCSha256 **hasher = (struct SCSha256 **)payload;
213  if (*hasher != NULL) {
214  SCSha256Free(*hasher);
215  *hasher = NULL;
216  }
217  break;
218  }
219  case SCLUA_TUD_SHA1: {
220  struct SCSha1 **hasher = (struct SCSha1 **)payload;
221  if (*hasher != NULL) {
222  SCSha1Free(*hasher);
223  *hasher = NULL;
224  }
225  break;
226  }
227  case SCLUA_TUD_MD5: {
228  struct SCMd5 **hasher = (struct SCMd5 **)payload;
229  if (*hasher != NULL) {
230  SCMd5Free(*hasher);
231  *hasher = NULL;
232  }
233  break;
234  }
235  default:
236  break;
237  }
238  return 0;
239 }
240 
241 static int LuaHashLibSha1New(lua_State *L)
242 {
243  if (CheckHashingEnabled(L)) {
244  return 1;
245  }
246  struct SCSha1 **hasher = SCLuaNewUserData(L, SCLUA_TUD_SHA1, sizeof(struct SCSha1 *));
247  if (hasher == NULL) {
248  return luaL_error(L, "failed to allocate userdata for sha1");
249  }
250  *hasher = SCSha1New();
251  luaL_getmetatable(L, SHA1_MT);
252  lua_setmetatable(L, -2);
253  return 1;
254 }
255 
256 static int LuaHashLibSha1Update(lua_State *L)
257 {
258  struct SCSha1 **hasher = SCLuaCheckUserData(L, 1, SHA1_MT, SCLUA_TUD_SHA1);
259  if (hasher == NULL || *hasher == NULL) {
260  return luaL_error(L, "invalid sha1 hash context");
261  }
262  if (*hasher == NULL) {
263  return luaL_error(L, "sha1 hasher already finalized");
264  }
265 
266  size_t data_len;
267  const char *data = luaL_checklstring(L, 2, &data_len);
268  if (CheckInputLength(L, data_len)) {
269  return 1;
270  }
271  SCSha1Update(*hasher, (const uint8_t *)data, (uint32_t)data_len);
272  return 0;
273 }
274 
275 static int LuaHashLibSha1Finalize(lua_State *L)
276 {
277  struct SCSha1 **hasher = SCLuaCheckUserData(L, 1, SHA1_MT, SCLUA_TUD_SHA1);
278  if (hasher == NULL || *hasher == NULL) {
279  return luaL_error(L, "invalid sha1 hash context");
280  }
281  if (*hasher == NULL) {
282  return luaL_error(L, "sha1 hasher already finalized");
283  }
284 
285  uint8_t hash[SC_SHA1_LEN];
286  struct SCSha1 *ctx = *hasher;
287  *hasher = NULL;
288  SCSha1Finalize(ctx, hash, sizeof(hash));
289  lua_pushlstring(L, (const char *)hash, sizeof(hash));
290 
291  return 1;
292 }
293 
294 static int LuaHashLibSha1FinalizeToHex(lua_State *L)
295 {
296  struct SCSha1 **hasher = SCLuaCheckUserData(L, 1, SHA1_MT, SCLUA_TUD_SHA1);
297  if (hasher == NULL || *hasher == NULL) {
298  return luaL_error(L, "invalid sha1 hash context");
299  }
300  if (*hasher == NULL) {
301  return luaL_error(L, "sha1 hasher already finalized");
302  }
303 
304  char hash[SC_SHA1_HEX_LEN + 1];
305  struct SCSha1 *ctx = *hasher;
306  *hasher = NULL;
307  if (!SCSha1FinalizeToHex(ctx, hash, sizeof(hash))) {
308  return luaL_error(L, "sha1 hashing failed");
309  }
310 
311  lua_pushstring(L, (const char *)hash);
312 
313  return 1;
314 }
315 
316 static int LuaHashLibSha1Digest(lua_State *L)
317 {
318  size_t buf_len;
319  const char *input = luaL_checklstring(L, 1, &buf_len);
320 
321  if (CheckHashingEnabled(L)) {
322  return 1;
323  }
324  if (CheckInputLength(L, buf_len)) {
325  return 1;
326  }
327 
328  uint8_t output[SC_SHA1_LEN];
329  if (!SCSha1HashBuffer((uint8_t *)input, (uint32_t)buf_len, output, sizeof(output))) {
330  return luaL_error(L, "sha1 hashing failed");
331  }
332 
333  lua_pushlstring(L, (const char *)output, sizeof(output));
334  return 1;
335 }
336 
337 static int LuaHashLibSha1HexDigest(lua_State *L)
338 {
339  size_t buf_len;
340  const char *input = luaL_checklstring(L, 1, &buf_len);
341 
342  if (CheckHashingEnabled(L)) {
343  return 1;
344  }
345  if (CheckInputLength(L, buf_len)) {
346  return 1;
347  }
348 
349  char output[SC_SHA1_HEX_LEN + 1];
350  if (!SCSha1HashBufferToHex((uint8_t *)input, (uint32_t)buf_len, output, sizeof(output))) {
351  return luaL_error(L, "sha1 hashing failed");
352  }
353 
354  lua_pushstring(L, (const char *)output);
355  return 1;
356 }
357 
358 static int LuaHashLibMd5New(lua_State *L)
359 {
360  if (CheckHashingEnabled(L)) {
361  return 1;
362  }
363  struct SCMd5 **hasher = SCLuaNewUserData(L, SCLUA_TUD_MD5, sizeof(struct SCMd5 *));
364  if (hasher == NULL) {
365  return luaL_error(L, "failed to allocate userdata for sha1");
366  }
367  *hasher = SCMd5New();
368  luaL_getmetatable(L, MD5_MT);
369  lua_setmetatable(L, -2);
370  return 1;
371 }
372 
373 static int LuaHashLibMd5Update(lua_State *L)
374 {
375  struct SCMd5 **hasher = SCLuaCheckUserData(L, 1, MD5_MT, SCLUA_TUD_MD5);
376  if (hasher == NULL || *hasher == NULL) {
377  return luaL_error(L, "invalid md5 hash context");
378  }
379  if (*hasher == NULL) {
380  return luaL_error(L, "md5 hasher already finalized");
381  }
382 
383  size_t data_len;
384  const char *data = luaL_checklstring(L, 2, &data_len);
385  if (CheckInputLength(L, data_len)) {
386  return 1;
387  }
388  SCMd5Update(*hasher, (const uint8_t *)data, (uint32_t)data_len);
389  return 0;
390 }
391 
392 static int LuaHashLibMd5Finalize(lua_State *L)
393 {
394  struct SCMd5 **hasher = SCLuaCheckUserData(L, 1, MD5_MT, SCLUA_TUD_MD5);
395  if (hasher == NULL || *hasher == NULL) {
396  return luaL_error(L, "invalid md5 hash context");
397  }
398  if (*hasher == NULL) {
399  return luaL_error(L, "md5 hasher already finalized");
400  }
401 
402  uint8_t hash[SC_MD5_LEN];
403  struct SCMd5 *ctx = *hasher;
404  *hasher = NULL;
405  SCMd5Finalize(ctx, hash, sizeof(hash));
406  lua_pushlstring(L, (const char *)hash, sizeof(hash));
407 
408  return 1;
409 }
410 
411 static int LuaHashLibMd5FinalizeToHex(lua_State *L)
412 {
413  struct SCMd5 **hasher = SCLuaCheckUserData(L, 1, MD5_MT, SCLUA_TUD_MD5);
414  if (hasher == NULL || *hasher == NULL) {
415  return luaL_error(L, "invalid md5 hash context");
416  }
417  if (*hasher == NULL) {
418  return luaL_error(L, "md5 hasher already finalized");
419  }
420 
421  char hash[SC_MD5_HEX_LEN + 1];
422  struct SCMd5 *ctx = *hasher;
423  *hasher = NULL;
424  if (!SCMd5FinalizeToHex(ctx, hash, sizeof(hash))) {
425  return luaL_error(L, "md5 hashing failed");
426  }
427 
428  lua_pushstring(L, (const char *)hash);
429 
430  return 1;
431 }
432 
433 static int LuaHashLibMd5Digest(lua_State *L)
434 {
435  size_t buf_len;
436  const char *input = luaL_checklstring(L, 1, &buf_len);
437 
438  if (CheckHashingEnabled(L)) {
439  return 1;
440  }
441  if (CheckInputLength(L, buf_len)) {
442  return 1;
443  }
444 
445  uint8_t output[SC_MD5_LEN];
446  if (!SCMd5HashBuffer((uint8_t *)input, (uint32_t)buf_len, output, sizeof(output))) {
447  return luaL_error(L, "md5 hashing failed");
448  }
449 
450  lua_pushlstring(L, (const char *)output, sizeof(output));
451  return 1;
452 }
453 
454 static int LuaHashLibMd5HexDigest(lua_State *L)
455 {
456  size_t buf_len;
457  const char *input = luaL_checklstring(L, 1, &buf_len);
458 
459  if (CheckHashingEnabled(L)) {
460  return 1;
461  }
462  if (CheckInputLength(L, buf_len)) {
463  return 1;
464  }
465 
466  char output[SC_MD5_HEX_LEN + 1];
467  if (!SCMd5HashBufferToHex((uint8_t *)input, (uint32_t)buf_len, output, sizeof(output))) {
468  return luaL_error(L, "md5 hashing failed");
469  }
470 
471  lua_pushstring(L, (const char *)output);
472  return 1;
473 }
474 
475 static const struct luaL_Reg hashlib[] = {
476  // clang-format off
477  { "sha256_digest", LuaHashLibSha256Digest },
478  { "sha256_hexdigest", LuaHashLibSha256HexDigest },
479  { "sha256", LuaHashLibSha256New },
480  { "sha1_digest", LuaHashLibSha1Digest },
481  { "sha1_hexdigest", LuaHashLibSha1HexDigest },
482  { "sha1", LuaHashLibSha1New },
483  { "md5_digest", LuaHashLibMd5Digest },
484  { "md5_hexdigest", LuaHashLibMd5HexDigest },
485  { "md5", LuaHashLibMd5New },
486  { NULL, NULL },
487  // clang-format on
488 };
489 
490 static const struct luaL_Reg sha256_methods[] = {
491  // clang-format off
492  { "update", LuaHashLibSha256Update },
493  { "finalize", LuaHashLibSha256Finalize },
494  { "finalize_to_hex", LuaHashLibSha256FinalizeToHex },
495  { NULL, NULL },
496  // clang-format on
497 };
498 
499 static const struct luaL_Reg sha1_methods[] = {
500  // clang-format off
501  { "update", LuaHashLibSha1Update },
502  { "finalize", LuaHashLibSha1Finalize },
503  { "finalize_to_hex", LuaHashLibSha1FinalizeToHex },
504  { NULL, NULL },
505  // clang-format on
506 };
507 
508 static const struct luaL_Reg md5_methods[] = {
509  // clang-format off
510  { "update", LuaHashLibMd5Update },
511  { "finalize", LuaHashLibMd5Finalize },
512  { "finalize_to_hex", LuaHashLibMd5FinalizeToHex },
513  { NULL, NULL },
514  // clang-format on
515 };
516 
518 {
519  luaL_newmetatable(L, SHA256_MT);
520  lua_pushcfunction(L, LuaHashLibGc);
521  lua_setfield(L, -2, "__gc");
522  luaL_newlib(L, sha256_methods);
523  lua_setfield(L, -2, "__index");
524 
525  luaL_newmetatable(L, SHA1_MT);
526  lua_pushcfunction(L, LuaHashLibGc);
527  lua_setfield(L, -2, "__gc");
528  luaL_newlib(L, sha1_methods);
529  lua_setfield(L, -2, "__index");
530 
531  luaL_newmetatable(L, MD5_MT);
532  lua_pushcfunction(L, LuaHashLibGc);
533  lua_setfield(L, -2, "__gc");
534  luaL_newlib(L, md5_methods);
535  lua_setfield(L, -2, "__index");
536 
537  luaL_newlib(L, hashlib);
538 
539  return 1;
540 }
SCSha1
struct SCSha1 SCSha1
Definition: util-file.h:106
SCLuaUserDataHeader_
Definition: util-lua-common.h:43
SCLuaUserDataHeader_::magic
uint32_t magic
Definition: util-lua-common.h:44
len
uint8_t len
Definition: app-layer-dnp3.h:2
util-lua-common.h
SC_SHA256_LEN
#define SC_SHA256_LEN
Definition: util-file.h:104
SC_SHA1_LEN
#define SC_SHA1_LEN
Definition: util-file.h:107
ctx
struct Thresholds ctx
SCLuaLoadHashlib
int SCLuaLoadHashlib(lua_State *L)
Definition: util-lua-hashlib.c:517
util-lua-hashlib.h
SCSha256
struct SCSha256 SCSha256
Definition: util-file.h:103
lua_State
struct lua_State lua_State
Definition: suricata-common.h:527
SCLuaCheckUserData
void * SCLuaCheckUserData(lua_State *L, int idx, const char *mt, uint32_t type_id)
Definition: util-lua-common.c:101
MD5_MT
#define MD5_MT
Definition: util-lua-hashlib.c:71
SCLUA_TUD_SHA256
@ SCLUA_TUD_SHA256
Definition: util-lua-common.h:50
SCLuaNewUserData
void * SCLuaNewUserData(lua_State *L, uint32_t type_id, size_t size)
Definition: util-lua-common.c:42
SHA256_MT
#define SHA256_MT
Definition: util-lua-hashlib.c:50
suricata-common.h
SCLUA_UD_MAGIC
#define SCLUA_UD_MAGIC
Definition: util-lua-common.h:41
SCLuaUserDataHeader_::type_id
uint32_t type_id
Definition: util-lua-common.h:45
SHA1_MT
#define SHA1_MT
Definition: util-lua-hashlib.c:70
SCLUA_TUD_MD5
@ SCLUA_TUD_MD5
Definition: util-lua-common.h:52
suricata.h
SC_MD5_LEN
#define SC_MD5_LEN
Definition: util-file.h:110
SCLUA_TUD_SHA1
@ SCLUA_TUD_SHA1
Definition: util-lua-common.h:51
SCMd5
struct SCMd5 SCMd5
Definition: util-file.h:109
g_disable_hashing
bool g_disable_hashing
Definition: suricata.c:219