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
lua_sha256_update(lua_State * L)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
lua_sha256_digest(lua_State * L)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
lua_sha256_hexdigest(lua_State * L)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
lua_sha256_done(lua_State * L)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
lua_sha256(lua_State * L)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
register_metatable_sha256(lua_State * L)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
luaopen_hash(lua_State * L)170 luaopen_hash(lua_State *L)
171 {
172 register_metatable_sha256(L);
173
174 luaL_newlib(L, hashlib);
175
176 return 1;
177 }
178