summaryrefslogtreecommitdiff
path: root/libexec/flua/libhash/lhash.c
blob: f455f006bf27aeca93451729d74c31ef0df27433 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
/*-
 * Copyright (c) 2024 Netflix, Inc
 *
 * SPDX-License-Identifier: BSD-2-Clause
 */

#include <lua.h>
#include "lauxlib.h"
#include "lhash.h"

#include <sha256.h>
#include <string.h>

#include "bootstrap.h"

#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;
}

#ifndef _STANDALONE
FLUA_MODULE(hash);
#endif