/** * $Id: md5lib.c,v 1.10 2008/05/12 20:51:27 carregal Exp $ * Cryptographic and Hash functions for Lua * @author Roberto Ierusalimschy */ #include #include #include #include "lua.h" #include "lauxlib.h" #include "md5.h" #define MAXKEY 256 #define BLOCKSIZE 16 /** * Hash function. Returns a hash for a given string. * @param message: arbitrary binary string. * @return A 128-bit hash string. */ static int lmd5 (lua_State *L) { char buff[16]; size_t l; const char *message = luaL_checklstring(L, 1, &l); md5((const unsigned char *)message, l, (unsigned char *)buff); lua_pushlstring(L, buff, 16L); return 1; } static int xmd5 (lua_State *L) { static char tab[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; int i; char buff[16]; char hexbuff[32]; size_t l; const char *message = luaL_checklstring(L, 1, &l); md5((const unsigned char *)message, l, (unsigned char *)buff); for (i = 0; i < 16; i++) { hexbuff[i*2] = tab[(buff[i] >> 4) & 15]; hexbuff[i*2+1] = tab[buff[i] & 15]; } lua_pushlstring(L, hexbuff, 32L); return 1; } /** * X-Or. Does a bit-a-bit exclusive-or of two strings. * @param s1: arbitrary binary string. * @param s2: arbitrary binary string with same length as s1. * @return a binary string with same length as s1 and s2, * where each bit is the exclusive-or of the corresponding bits in s1-s2. */ static int ex_or (lua_State *L) { size_t l1, l2; const char *s1 = luaL_checklstring(L, 1, &l1); const char *s2 = luaL_checklstring(L, 2, &l2); luaL_Buffer b; luaL_argcheck( L, l1 == l2, 2, "lengths must be equal" ); luaL_buffinit(L, &b); while (l1--) luaL_addchar(&b, (*s1++)^(*s2++)); luaL_pushresult(&b); return 1; } static void checkseed (lua_State *L) { if (lua_isnone(L, 3)) { /* no seed? */ time_t tm = time(NULL); /* for `random' seed */ lua_pushlstring(L, (char *)&tm, sizeof(tm)); } } // Singe: Lua 5.2 and later provide luaL_setfuncs, so this stand-in is only for 5.1. #if LUA_VERSION_NUM < 502 void luaL_setfuncs (lua_State *L, const luaL_Reg *l, int nup) { luaL_checkstack(L, nup+1, "too many upvalues"); for (; l->name != NULL; l++) { /* fill the table with given functions */ int i; lua_pushstring(L, l->name); for (i = 0; i < nup; i++) /* copy upvalues to the top */ lua_pushvalue(L, -(nup + 1)); lua_pushcclosure(L, l->func, nup); /* closure with those upvalues */ lua_settable(L, -(nup + 3)); /* table must be below the upvalues, the name and the closure */ } lua_pop(L, nup); /* remove upvalues */ } #endif static int initblock (lua_State *L, const char *seed, int lseed, char *block) { size_t lkey; const char *key = luaL_checklstring(L, 2, &lkey); if (lkey > MAXKEY) luaL_error(L, "key too long (> %d)", MAXKEY); memset(block, 0, BLOCKSIZE); memcpy(block, seed, lseed); memcpy(block+BLOCKSIZE, key, lkey); return (int)lkey+BLOCKSIZE; } static void codestream (lua_State *L, const char *msg, size_t lmsg, char *block, int lblock) { luaL_Buffer b; luaL_buffinit(L, &b); while (lmsg > 0) { char code[BLOCKSIZE]; int i; md5((const unsigned char *)block, lblock, (unsigned char *)code); for (i=0; i 0; i++, lmsg--) code[i] ^= *msg++; luaL_addlstring(&b, code, i); memcpy(block, code, i); /* update seed */ } luaL_pushresult(&b); } static void decodestream (lua_State *L, const char *cypher, size_t lcypher, char *block, int lblock) { luaL_Buffer b; luaL_buffinit(L, &b); while (lcypher > 0) { char code[BLOCKSIZE]; int i; md5((const unsigned char *)block, lblock, (unsigned char *)code); /* update seed */ for (i=0; i 0; i++, lcypher--) code[i] ^= *cypher++; luaL_addlstring(&b, code, i); memcpy(block, cypher-i, i); } luaL_pushresult(&b); } /** * Encrypts a string. Uses the hash function md5 in CFB (Cipher-feedback * mode). * @param message: arbitrary binary string to be encrypted. * @param key: arbitrary binary string to be used as a key. * @param [seed]: optional arbitrary binary string to be used as a seed. * if no seed is provided, the function uses the result of * time() as a seed. * @return The cyphertext (as a binary string). */ static int crypt (lua_State *L) { size_t lmsg; const char *msg = luaL_checklstring(L, 1, &lmsg); size_t lseed; const char *seed; int lblock; char block[BLOCKSIZE+MAXKEY]; checkseed(L); seed = luaL_checklstring(L, 3, &lseed); if (lseed > BLOCKSIZE) luaL_error(L, "seed too long (> %d)", BLOCKSIZE); /* put seed and seed length at the beginning of result */ block[0] = (char)lseed; memcpy(block+1, seed, lseed); lua_pushlstring(L, block, lseed+1); /* to concat with result */ lblock = initblock(L, seed, lseed, block); codestream(L, msg, lmsg, block, lblock); lua_concat(L, 2); return 1; } /** * Decrypts a string. For any message, key, and seed, we have that * decrypt(crypt(msg, key, seed), key) == msg. * @param cyphertext: message to be decrypted (this must be the result of a previous call to crypt. * @param key: arbitrary binary string to be used as a key. * @return The plaintext. */ static int decrypt (lua_State *L) { size_t lcyphertext; const char *cyphertext = luaL_checklstring(L, 1, &lcyphertext); size_t lseed = cyphertext[0]; const char *seed = cyphertext+1; int lblock; char block[BLOCKSIZE+MAXKEY]; if (!(lcyphertext >= lseed + 1 && lseed <= BLOCKSIZE)) { lua_pushlstring(L, NULL, 0); lua_pushboolean(L, 0); return 2; } cyphertext += lseed+1; lcyphertext -= lseed+1; lblock = initblock(L, seed, lseed, block); decodestream(L, cyphertext, lcyphertext, block, lblock); lua_pushboolean(L, 1); return 2; } /* ** Assumes the table is on top of the stack. */ static void set_info (lua_State *L) { lua_pushliteral (L, "_COPYRIGHT"); lua_pushliteral (L, "Copyright (C) 2003-2019 PUC-Rio"); lua_settable (L, -3); lua_pushliteral (L, "_DESCRIPTION"); lua_pushliteral (L, "Checksum facilities for Lua"); lua_settable (L, -3); lua_pushliteral (L, "_VERSION"); lua_pushliteral (L, "MD5 1.3"); lua_settable (L, -3); } static struct luaL_Reg md5lib[] = { {"sum", lmd5}, {"hexsum", xmd5}, {"exor", ex_or}, {"crypt", crypt}, {"decrypt", decrypt}, {NULL, NULL} }; int luaopen_md5_core (lua_State *L) { #if LUA_VERSION_NUM < 502 lua_newtable(L); luaL_register(L, "md5", md5lib); luaL_setfuncs(L, md5lib, 0); #else /* Singe: 5.2 dropped luaL_register. luaL_newlib makes the table and fills it, and the module system binds the name, so nothing is written to the globals here. */ luaL_newlib(L, md5lib); #endif set_info (L); return 1; }