diff --git a/lib/flua/Makefile b/lib/flua/Makefile index eb148c2125fd..769736039f7e 100644 --- a/lib/flua/Makefile +++ b/lib/flua/Makefile @@ -1,4 +1,4 @@ - -SUBDIR= libjail +SUBDIR+= libhash +SUBDIR+= libjail .include diff --git a/lib/flua/libhash/Makefile b/lib/flua/libhash/Makefile new file mode 100644 index 000000000000..f166ff90a392 --- /dev/null +++ b/lib/flua/libhash/Makefile @@ -0,0 +1,14 @@ +SHLIB_NAME= hash.so +SHLIBDIR= ${LIBDIR}/flua + +SRCS+= lhash.c + +CFLAGS+= \ + -I${SRCTOP}/contrib/lua/src \ + -I${SRCTOP}/lib/liblua \ + +LIBADD+= md + +MAN= hash.3lua + +.include diff --git a/lib/flua/libhash/hash.3lua b/lib/flua/libhash/hash.3lua new file mode 100644 index 000000000000..1662e87f7c68 --- /dev/null +++ b/lib/flua/libhash/hash.3lua @@ -0,0 +1,54 @@ +.\" +.\" Copyright (c) 2024 Netflix, Inc. +.\" +.\" SPDX-License-Identifier: BSD-2-Clause +.\" +.Dd February 6, 2024 +.Dt HASH 3lua +.Os +.Sh NAME +.Nm new , +.Nm update , +.Nm digest , +.Nm hexdigest +.Nd Lua Cryptographic hash module. +.Sh DESCRIPTION +The built-in cryptographic hashing Lua bindings for the are available via the +.Ic hash +table. +.Ss Supported Hashing Schemes +The following hashing schemes are supported by the hash module. +.Bl -bullet -compact +.It +sha256 +.El +.Ss APIs Supported +.Bl -tag -width asdf -compact +.It Fn new data +Compute a digest based on the +.Va data . +.It Fn update Va data +Using the current digest, process +.Va data +to compute a new digest as if all prior data had been concatenated together. +.It Fn digest +Return the hashed digest as a binary array. +This resets the context. +.It Fn hexdigest +Take +.Fn digest +and convert it to an upper case hex string. +This resets the context. +.It Va digest_size +Return the size of the digest, in bytes. +.It Va block_size +Return the block size used in bytes. +.El +.Sh EXAMPLES +.Sh SEE ALSO +.Xr sha256 3 +.Sh AUTHORS +The +.Nm +man page was written by +.An Warner Losh Aq Mt imp@FreeBSD.org . diff --git a/lib/flua/libhash/lhash.c b/lib/flua/libhash/lhash.c new file mode 100644 index 000000000000..4587961fe8a0 --- /dev/null +++ b/lib/flua/libhash/lhash.c @@ -0,0 +1,177 @@ +/*- + * Copyright (c) 2024 Netflix, Inc + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include "lauxlib.h" +#include "lhash.h" + +#include +#include + +#define SHA256_META "SHA256 meta table" +#define SHA256_DIGEST_LEN 32 + +/* + * Note C++ comments indicate the before -- after state of the stack, in with a + * similar convention to forth's ( ) comments. Lua indexes are from 1 and can be + * read left to right (leftmost is 1). Negative are relative to the end (-1 is + * rightmost). A '.' indicates a return value left on the stack (all values to + * its right). Trivial functions don't do this. + */ + +/* + * Updates the digest with the new data passed in. Takes 1 argument, which + * is converted to a string. + */ +static int +lua_sha256_update(lua_State *L) +{ + size_t len; + const unsigned char *data; + SHA256_CTX *ctx; + + ctx = luaL_checkudata(L, 1, SHA256_META); + data = luaL_checklstring(L, 2, &len); + SHA256_Update(ctx, data, len); + + lua_settop(L, 1); + + return (1); +} + +/* + * Finalizes the digest value and returns it as a 32-byte binary string. The ctx + * is zeroed. + */ +static int +lua_sha256_digest(lua_State *L) +{ + SHA256_CTX *ctx; + unsigned char digest[SHA256_DIGEST_LEN]; + + ctx = luaL_checkudata(L, 1, SHA256_META); + SHA256_Final(digest, ctx); + lua_pushlstring(L, digest, sizeof(digest)); + + return (1); +} + +/* + * Finalizes the digest value and returns it as a 64-byte ascii string of hex + * numbers. The ctx is zeroed. + */ +static int +lua_sha256_hexdigest(lua_State *L) +{ + SHA256_CTX *ctx; + char buf[SHA256_DIGEST_LEN * 2 + 1]; + unsigned char digest[SHA256_DIGEST_LEN]; + static const char hex[]="0123456789abcdef"; + int i; + + ctx = luaL_checkudata(L, 1, SHA256_META); + SHA256_Final(digest, ctx); + for (i = 0; i < SHA256_DIGEST_LEN; i++) { + buf[i+i] = hex[digest[i] >> 4]; + buf[i+i+1] = hex[digest[i] & 0x0f]; + } + buf[i+i] = '\0'; + + lua_pushstring(L, buf); + + return (1); +} + +/* + * Zeros out the ctx before garbage collection. Normally this is done in + * obj:digest or obj:hexdigest, but if not, it will be wiped here. Lua + * manages freeing the ctx memory. + */ +static int +lua_sha256_done(lua_State *L) +{ + SHA256_CTX *ctx; + + ctx = luaL_checkudata(L, 1, SHA256_META); + memset(ctx, 0, sizeof(*ctx)); + + return (0); +} + +/* + * Create object obj which accumulates the state of the sha256 digest + * for its contents and any subsequent obj:update call. It takes zero + * or 1 arguments. + */ +static int +lua_sha256(lua_State *L) +{ + SHA256_CTX *ctx; + int top; + + /* We take 0 or 1 args */ + top = lua_gettop(L); // data -- data + if (top > 1) { + lua_pushnil(L); + return (1); + } + + ctx = lua_newuserdata(L, sizeof(*ctx)); // data -- data ctx + SHA256_Init(ctx); + if (top == 1) { + size_t len; + const unsigned char *data; + + data = luaL_checklstring(L, 1, &len); + SHA256_Update(ctx, data, len); + } + luaL_setmetatable(L, SHA256_META); // data ctx -- data ctx + + return (1); // data . ctx +} + +/* + * Setup the metatable to manage our userdata that we create in lua_sha256. We + * request a finalization call with __gc so we can zero out the ctx buffer so + * that we don't leak secrets if obj:digest or obj:hexdigest aren't called. + */ +static void +register_metatable_sha256(lua_State *L) +{ + luaL_newmetatable(L, SHA256_META); // -- meta + + lua_newtable(L); // meta -- meta tbl + lua_pushcfunction(L, lua_sha256_update); // meta tbl -- meta tbl fn + lua_setfield(L, -2, "update"); // meta tbl fn -- meta tbl + lua_pushcfunction(L, lua_sha256_digest); // meta tbl -- meta tbl fn + lua_setfield(L, -2, "digest"); // meta tbl fn -- meta tbl + lua_pushcfunction(L, lua_sha256_hexdigest); // meta tbl -- meta tbl fn + lua_setfield(L, -2, "hexdigest"); // meta tbl fn -- meta tbl + + /* Associate tbl with metatable */ + lua_setfield(L, -2, "__index"); // meta tbl -- meta + lua_pushcfunction(L, lua_sha256_done); // meta -- meta fn + lua_setfield(L, -2, "__gc"); // meta fn -- meta + + lua_pop(L, 1); // meta -- +} + +#define REG_SIMPLE(n) { #n, lua_ ## n } +static const struct luaL_Reg hashlib[] = { + REG_SIMPLE(sha256), + { NULL, NULL }, +}; +#undef REG_SIMPLE + +int +luaopen_hash(lua_State *L) +{ + register_metatable_sha256(L); + + luaL_newlib(L, hashlib); + + return 1; +} diff --git a/lib/flua/libhash/lhash.h b/lib/flua/libhash/lhash.h new file mode 100644 index 000000000000..c1e9788a55a3 --- /dev/null +++ b/lib/flua/libhash/lhash.h @@ -0,0 +1,11 @@ +/*- + * Copyright (c) 2024 Netflix, Inc + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include + +int luaopen_hash(lua_State *L);