/**
* $Id: md5lib.c,v 1.1 2000/11/09 19:19:59 roberto Exp $
* Cryptographic and Hash functions for Lua
* @version 1.0
* @author Roberto Ierusalimschy
*/
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <lua.h>
#include <lauxlib.h>
#include "md5.h"
/**
* 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_check_lstr(L, 1, &l);
md5(message, l, buff);
lua_pushlstring(L, buff, 16L);
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_check_lstr(L, 1, &l1);
const char *s2 = luaL_check_lstr(L, 2, &l2);
luaL_Buffer b;
luaL_arg_check( L, l1 == l2, 2, "lengths must be equal" );
luaL_buffinit(L, &b);
while (l1--) luaL_putchar(&b, (*s1++)^(*s2++));
luaL_pushresult(&b);
return 1;
}
static void checkseed (lua_State *L) {
if (lua_isnull(L, 3)) { /* no seed? */
time_t tm = time(NULL); /* for `random' seed */
lua_pushlstring(L, (char *)&tm, sizeof(tm));
}
}
#define MAXKEY 256
#define BLOCKSIZE 16
static int initblock (lua_State *L, const char *seed, int lseed, char *block) {
size_t lkey;
const char *key = luaL_check_lstr(L, 2, &lkey);
if (lkey > MAXKEY)
luaL_verror(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(block, lblock, 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(block, lblock, 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_check_lstr(L, 1, &lmsg);
size_t lseed;
const char *seed;
int lblock;
char block[BLOCKSIZE+MAXKEY];
checkseed(L);
seed = luaL_check_lstr(L, 3, &lseed);
if (lseed > BLOCKSIZE)
luaL_verror(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_check_lstr(L, 1, &lcyphertext);
size_t lseed = cyphertext[0];
const char *seed = cyphertext+1;
int lblock;
char block[BLOCKSIZE+MAXKEY];
luaL_arg_check(L, lcyphertext >= lseed+1 && lseed <= BLOCKSIZE, 1,
"invalid cyphered string");
cyphertext += lseed+1;
lcyphertext -= lseed+1;
lblock = initblock(L, seed, lseed, block);
decodestream(L, cyphertext, lcyphertext, block, lblock);
return 1;
}
static struct luaL_reg md5lib[] = {
{"md5", lmd5},
{"exor", ex_or},
{"crypt", crypt},
{"decrypt", decrypt}
};
void md5lib_open (lua_State *L) {
luaL_openlib( L, md5lib, (sizeof(md5lib)/sizeof(md5lib[0])));
}