/*-1* Copyright (c) 2024 Netflix, Inc2*3* SPDX-License-Identifier: BSD-2-Clause4*/56#include <lua.h>7#include "lauxlib.h"8#include "lhash.h"910#include <sha256.h>11#include <string.h>1213#define SHA256_META "SHA256 meta table"14#define SHA256_DIGEST_LEN 321516/*17* Note C++ comments indicate the before -- after state of the stack, in with a18* similar convention to forth's ( ) comments. Lua indexes are from 1 and can be19* read left to right (leftmost is 1). Negative are relative to the end (-1 is20* rightmost). A '.' indicates a return value left on the stack (all values to21* its right). Trivial functions don't do this.22*/2324/*25* Updates the digest with the new data passed in. Takes 1 argument, which26* is converted to a string.27*/28static int29lua_sha256_update(lua_State *L)30{31size_t len;32const unsigned char *data;33SHA256_CTX *ctx;3435ctx = luaL_checkudata(L, 1, SHA256_META);36data = luaL_checklstring(L, 2, &len);37SHA256_Update(ctx, data, len);3839lua_settop(L, 1);4041return (1);42}4344/*45* Finalizes the digest value and returns it as a 32-byte binary string. The ctx46* is zeroed.47*/48static int49lua_sha256_digest(lua_State *L)50{51SHA256_CTX *ctx;52unsigned char digest[SHA256_DIGEST_LEN];5354ctx = luaL_checkudata(L, 1, SHA256_META);55SHA256_Final(digest, ctx);56lua_pushlstring(L, digest, sizeof(digest));5758return (1);59}6061/*62* Finalizes the digest value and returns it as a 64-byte ascii string of hex63* numbers. The ctx is zeroed.64*/65static int66lua_sha256_hexdigest(lua_State *L)67{68SHA256_CTX *ctx;69char buf[SHA256_DIGEST_LEN * 2 + 1];70unsigned char digest[SHA256_DIGEST_LEN];71static const char hex[]="0123456789abcdef";72int i;7374ctx = luaL_checkudata(L, 1, SHA256_META);75SHA256_Final(digest, ctx);76for (i = 0; i < SHA256_DIGEST_LEN; i++) {77buf[i+i] = hex[digest[i] >> 4];78buf[i+i+1] = hex[digest[i] & 0x0f];79}80buf[i+i] = '\0';8182lua_pushstring(L, buf);8384return (1);85}8687/*88* Zeros out the ctx before garbage collection. Normally this is done in89* obj:digest or obj:hexdigest, but if not, it will be wiped here. Lua90* manages freeing the ctx memory.91*/92static int93lua_sha256_done(lua_State *L)94{95SHA256_CTX *ctx;9697ctx = luaL_checkudata(L, 1, SHA256_META);98memset(ctx, 0, sizeof(*ctx));99100return (0);101}102103/*104* Create object obj which accumulates the state of the sha256 digest105* for its contents and any subsequent obj:update call. It takes zero106* or 1 arguments.107*/108static int109lua_sha256(lua_State *L)110{111SHA256_CTX *ctx;112int top;113114/* We take 0 or 1 args */115top = lua_gettop(L); // data -- data116if (top > 1) {117lua_pushnil(L);118return (1);119}120121ctx = lua_newuserdata(L, sizeof(*ctx)); // data -- data ctx122SHA256_Init(ctx);123if (top == 1) {124size_t len;125const unsigned char *data;126127data = luaL_checklstring(L, 1, &len);128SHA256_Update(ctx, data, len);129}130luaL_setmetatable(L, SHA256_META); // data ctx -- data ctx131132return (1); // data . ctx133}134135/*136* Setup the metatable to manage our userdata that we create in lua_sha256. We137* request a finalization call with __gc so we can zero out the ctx buffer so138* that we don't leak secrets if obj:digest or obj:hexdigest aren't called.139*/140static void141register_metatable_sha256(lua_State *L)142{143luaL_newmetatable(L, SHA256_META); // -- meta144145lua_newtable(L); // meta -- meta tbl146lua_pushcfunction(L, lua_sha256_update); // meta tbl -- meta tbl fn147lua_setfield(L, -2, "update"); // meta tbl fn -- meta tbl148lua_pushcfunction(L, lua_sha256_digest); // meta tbl -- meta tbl fn149lua_setfield(L, -2, "digest"); // meta tbl fn -- meta tbl150lua_pushcfunction(L, lua_sha256_hexdigest); // meta tbl -- meta tbl fn151lua_setfield(L, -2, "hexdigest"); // meta tbl fn -- meta tbl152153/* Associate tbl with metatable */154lua_setfield(L, -2, "__index"); // meta tbl -- meta155lua_pushcfunction(L, lua_sha256_done); // meta -- meta fn156lua_setfield(L, -2, "__gc"); // meta fn -- meta157158lua_pop(L, 1); // meta --159}160161#define REG_SIMPLE(n) { #n, lua_ ## n }162static const struct luaL_Reg hashlib[] = {163REG_SIMPLE(sha256),164{ NULL, NULL },165};166#undef REG_SIMPLE167168int169luaopen_hash(lua_State *L)170{171register_metatable_sha256(L);172173luaL_newlib(L, hashlib);174175return 1;176}177178179