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 */
29static int
30lua_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 */
49static int
50lua_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 */
66static int
67lua_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 */
93static int
94lua_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 */
109static int
110lua_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 */
141static void
142register_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 }
163static const struct luaL_Reg hashlib[] = {
164	REG_SIMPLE(sha256),
165	{ NULL, NULL },
166};
167#undef REG_SIMPLE
168
169int
170luaopen_hash(lua_State *L)
171{
172	register_metatable_sha256(L);
173
174	luaL_newlib(L, hashlib);
175
176	return 1;
177}
178