/*-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#include "bootstrap.h"1415#define SHA256_META "SHA256 meta table"16#define SHA256_DIGEST_LEN 321718/*19* Note C++ comments indicate the before -- after state of the stack, in with a20* similar convention to forth's ( ) comments. Lua indexes are from 1 and can be21* read left to right (leftmost is 1). Negative are relative to the end (-1 is22* rightmost). A '.' indicates a return value left on the stack (all values to23* its right). Trivial functions don't do this.24*/2526/*27* Updates the digest with the new data passed in. Takes 1 argument, which28* is converted to a string.29*/30static int31lua_sha256_update(lua_State *L)32{33size_t len;34const unsigned char *data;35SHA256_CTX *ctx;3637ctx = luaL_checkudata(L, 1, SHA256_META);38data = luaL_checklstring(L, 2, &len);39SHA256_Update(ctx, data, len);4041lua_settop(L, 1);4243return (1);44}4546/*47* Finalizes the digest value and returns it as a 32-byte binary string. The ctx48* is zeroed.49*/50static int51lua_sha256_digest(lua_State *L)52{53SHA256_CTX *ctx;54unsigned char digest[SHA256_DIGEST_LEN];5556ctx = luaL_checkudata(L, 1, SHA256_META);57SHA256_Final(digest, ctx);58lua_pushlstring(L, digest, sizeof(digest));5960return (1);61}6263/*64* Finalizes the digest value and returns it as a 64-byte ascii string of hex65* numbers. The ctx is zeroed.66*/67static int68lua_sha256_hexdigest(lua_State *L)69{70SHA256_CTX *ctx;71char buf[SHA256_DIGEST_LEN * 2 + 1];72unsigned char digest[SHA256_DIGEST_LEN];73static const char hex[]="0123456789abcdef";74int i;7576ctx = luaL_checkudata(L, 1, SHA256_META);77SHA256_Final(digest, ctx);78for (i = 0; i < SHA256_DIGEST_LEN; i++) {79buf[i+i] = hex[digest[i] >> 4];80buf[i+i+1] = hex[digest[i] & 0x0f];81}82buf[i+i] = '\0';8384lua_pushstring(L, buf);8586return (1);87}8889/*90* Zeros out the ctx before garbage collection. Normally this is done in91* obj:digest or obj:hexdigest, but if not, it will be wiped here. Lua92* manages freeing the ctx memory.93*/94static int95lua_sha256_done(lua_State *L)96{97SHA256_CTX *ctx;9899ctx = luaL_checkudata(L, 1, SHA256_META);100memset(ctx, 0, sizeof(*ctx));101102return (0);103}104105/*106* Create object obj which accumulates the state of the sha256 digest107* for its contents and any subsequent obj:update call. It takes zero108* or 1 arguments.109*/110static int111lua_sha256(lua_State *L)112{113SHA256_CTX *ctx;114int top;115116/* We take 0 or 1 args */117top = lua_gettop(L); // data -- data118if (top > 1) {119lua_pushnil(L);120return (1);121}122123ctx = lua_newuserdata(L, sizeof(*ctx)); // data -- data ctx124SHA256_Init(ctx);125if (top == 1) {126size_t len;127const unsigned char *data;128129data = luaL_checklstring(L, 1, &len);130SHA256_Update(ctx, data, len);131}132luaL_setmetatable(L, SHA256_META); // data ctx -- data ctx133134return (1); // data . ctx135}136137/*138* Setup the metatable to manage our userdata that we create in lua_sha256. We139* request a finalization call with __gc so we can zero out the ctx buffer so140* that we don't leak secrets if obj:digest or obj:hexdigest aren't called.141*/142static void143register_metatable_sha256(lua_State *L)144{145luaL_newmetatable(L, SHA256_META); // -- meta146147lua_newtable(L); // meta -- meta tbl148lua_pushcfunction(L, lua_sha256_update); // meta tbl -- meta tbl fn149lua_setfield(L, -2, "update"); // meta tbl fn -- meta tbl150lua_pushcfunction(L, lua_sha256_digest); // meta tbl -- meta tbl fn151lua_setfield(L, -2, "digest"); // meta tbl fn -- meta tbl152lua_pushcfunction(L, lua_sha256_hexdigest); // meta tbl -- meta tbl fn153lua_setfield(L, -2, "hexdigest"); // meta tbl fn -- meta tbl154155/* Associate tbl with metatable */156lua_setfield(L, -2, "__index"); // meta tbl -- meta157lua_pushcfunction(L, lua_sha256_done); // meta -- meta fn158lua_setfield(L, -2, "__gc"); // meta fn -- meta159160lua_pop(L, 1); // meta --161}162163#define REG_SIMPLE(n) { #n, lua_ ## n }164static const struct luaL_Reg hashlib[] = {165REG_SIMPLE(sha256),166{ NULL, NULL },167};168#undef REG_SIMPLE169170int171luaopen_hash(lua_State *L)172{173register_metatable_sha256(L);174175luaL_newlib(L, hashlib);176177return 1;178}179180#ifndef _STANDALONE181FLUA_MODULE(hash);182#endif183184185