xref: /freebsd/libexec/flua/libhash/lhash.c (revision b11a5709ec2b61fefb03bfdd38e2f06d2c1107c1)
1 /*-
2  * Copyright (c) 2024 Netflix, Inc
3  *
4  * SPDX-License-Identifier: BSD-2-Clause
5  */
6 
7 #include <lua.h>
8 #include "lauxlib.h"
9 #include "lhash.h"
10 
11 #include <sha256.h>
12 #include <string.h>
13 
14 #include "bootstrap.h"
15 
16 #define SHA256_META "SHA256 meta table"
17 #define SHA256_DIGEST_LEN 32
18 
19 /*
20  * Note C++ comments indicate the before -- after state of the stack, in with a
21  * similar convention to forth's ( ) comments. Lua indexes are from 1 and can be
22  * read left to right (leftmost is 1). Negative are relative to the end (-1 is
23  * rightmost). A '.' indicates a return value left on the stack (all values to
24  * its right). Trivial functions don't do this.
25  */
26 
27 /*
28  * Updates the digest with the new data passed in. Takes 1 argument, which
29  * is converted to a string.
30  */
31 static int
lua_sha256_update(lua_State * L)32 lua_sha256_update(lua_State *L)
33 {
34 	size_t len;
35 	const unsigned char *data;
36 	SHA256_CTX *ctx;
37 
38 	ctx = luaL_checkudata(L, 1, SHA256_META);
39 	data = luaL_checklstring(L, 2, &len);
40 	SHA256_Update(ctx, data, len);
41 
42 	lua_settop(L, 1);
43 
44 	return (1);
45 }
46 
47 /*
48  * Finalizes the digest value and returns it as a 32-byte binary string. The ctx
49  * is zeroed.
50  */
51 static int
lua_sha256_digest(lua_State * L)52 lua_sha256_digest(lua_State *L)
53 {
54 	SHA256_CTX *ctx;
55 	unsigned char digest[SHA256_DIGEST_LEN];
56 
57 	ctx = luaL_checkudata(L, 1, SHA256_META);
58 	SHA256_Final(digest, ctx);
59 	lua_pushlstring(L, digest, sizeof(digest));
60 
61 	return (1);
62 }
63 
64 /*
65  * Finalizes the digest value and returns it as a 64-byte ascii string of hex
66  * numbers. The ctx is zeroed.
67  */
68 static int
lua_sha256_hexdigest(lua_State * L)69 lua_sha256_hexdigest(lua_State *L)
70 {
71 	SHA256_CTX *ctx;
72 	char buf[SHA256_DIGEST_LEN * 2 + 1];
73 	unsigned char digest[SHA256_DIGEST_LEN];
74 	static const char hex[]="0123456789abcdef";
75 	int i;
76 
77 	ctx = luaL_checkudata(L, 1, SHA256_META);
78 	SHA256_Final(digest, ctx);
79 	for (i = 0; i < SHA256_DIGEST_LEN; i++) {
80 		buf[i+i] = hex[digest[i] >> 4];
81 		buf[i+i+1] = hex[digest[i] & 0x0f];
82 	}
83 	buf[i+i] = '\0';
84 
85 	lua_pushstring(L, buf);
86 
87 	return (1);
88 }
89 
90 /*
91  * Zeros out the ctx before garbage collection. Normally this is done in
92  * obj:digest or obj:hexdigest, but if not, it will be wiped here. Lua
93  * manages freeing the ctx memory.
94  */
95 static int
lua_sha256_done(lua_State * L)96 lua_sha256_done(lua_State *L)
97 {
98 	SHA256_CTX *ctx;
99 
100 	ctx = luaL_checkudata(L, 1, SHA256_META);
101 	memset(ctx, 0, sizeof(*ctx));
102 
103 	return (0);
104 }
105 
106 /*
107  * Create object obj which accumulates the state of the sha256 digest
108  * for its contents and any subsequent obj:update call. It takes zero
109  * or 1 arguments.
110  */
111 static int
lua_sha256(lua_State * L)112 lua_sha256(lua_State *L)
113 {
114 	SHA256_CTX *ctx;
115 	int top;
116 
117 	/* We take 0 or 1 args */
118 	top = lua_gettop(L);				// data -- data
119 	if (top > 1) {
120 		lua_pushnil(L);
121 		return (1);
122 	}
123 
124 	ctx = lua_newuserdata(L, sizeof(*ctx));		// data -- data ctx
125 	SHA256_Init(ctx);
126 	if (top == 1) {
127 		size_t len;
128 		const unsigned char *data;
129 
130 		data = luaL_checklstring(L, 1, &len);
131 		SHA256_Update(ctx, data, len);
132 	}
133 	luaL_setmetatable(L, SHA256_META);		// data ctx -- data ctx
134 
135 	return (1);					// data . ctx
136 }
137 
138 /*
139  * Setup the metatable to manage our userdata that we create in lua_sha256. We
140  * request a finalization call with __gc so we can zero out the ctx buffer so
141  * that we don't leak secrets if obj:digest or obj:hexdigest aren't called.
142  */
143 static void
register_metatable_sha256(lua_State * L)144 register_metatable_sha256(lua_State *L)
145 {
146 	luaL_newmetatable(L, SHA256_META);		// -- meta
147 
148 	lua_newtable(L);				// meta -- meta tbl
149 	lua_pushcfunction(L, lua_sha256_update);	// meta tbl -- meta tbl fn
150 	lua_setfield(L, -2, "update");			// meta tbl fn -- meta tbl
151 	lua_pushcfunction(L, lua_sha256_digest);	// meta tbl -- meta tbl fn
152 	lua_setfield(L, -2, "digest");			// meta tbl fn -- meta tbl
153 	lua_pushcfunction(L, lua_sha256_hexdigest);	// meta tbl -- meta tbl fn
154 	lua_setfield(L, -2, "hexdigest");		// meta tbl fn -- meta tbl
155 
156 	/* Associate tbl with metatable */
157 	lua_setfield(L, -2, "__index");			// meta tbl -- meta
158 	lua_pushcfunction(L, lua_sha256_done);		// meta -- meta fn
159 	lua_setfield(L, -2, "__gc");			// meta fn -- meta
160 
161 	lua_pop(L, 1);					// meta --
162 }
163 
164 #define REG_SIMPLE(n)	{ #n, lua_ ## n }
165 static const struct luaL_Reg hashlib[] = {
166 	REG_SIMPLE(sha256),
167 	{ NULL, NULL },
168 };
169 #undef REG_SIMPLE
170 
171 int
luaopen_hash(lua_State * L)172 luaopen_hash(lua_State *L)
173 {
174 	register_metatable_sha256(L);
175 
176 	luaL_newlib(L, hashlib);
177 
178 	return 1;
179 }
180 
181 #ifndef _STANDALONE
182 FLUA_MODULE(hash);
183 #endif
184