241 lines
6.8 KiB
C
241 lines
6.8 KiB
C
/**
|
|
* $Id: md5lib.c,v 1.10 2008/05/12 20:51:27 carregal Exp $
|
|
* Cryptographic and Hash functions for Lua
|
|
* @author Roberto Ierusalimschy
|
|
*/
|
|
|
|
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <time.h>
|
|
|
|
#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<BLOCKSIZE && lmsg > 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<BLOCKSIZE && lcypher > 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
|
|
* <code>time()</code> 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
|
|
* <code>decrypt(crypt(msg, key, seed), key) == msg</code>.
|
|
* @param cyphertext: message to be decrypted (this must be the result of
|
|
a previous call to <code>crypt</code>.
|
|
* @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;
|
|
}
|