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