1 /**
2 *  $Id: md5lib.c,v 1.2 2003/04/28 16:25:44 roberto Exp $
3 *  Cryptographic and Hash functions for Lua
4 *  @version  1.0
5 *  @author  Roberto Ierusalimschy
6 */
7 
8 
9 #include <stdlib.h>
10 #include <string.h>
11 #include <time.h>
12 
13 #include <lua.h>
14 #include <lauxlib.h>
15 
16 #include "luamd5.h"
17 
18 
19 /**
20 *  Hash function. Returns a hash for a given string.
21 *  @param message: arbitrary binary string.
22 *  @return  A 128-bit hash string.
23 */
lmd5(lua_State * L)24 static int lmd5 (lua_State *L) {
25   char buff[16];
26   size_t l;
27   const char *message = luaL_checklstring(L, 1, &l);
28   md5(message, l, buff);
29   lua_pushlstring(L, buff, 16L);
30   return 1;
31 }
32 
33 
34 /**
35 *  X-Or. Does a bit-a-bit exclusive-or of two strings.
36 *  @param s1: arbitrary binary string.
37 *  @param s2: arbitrary binary string with same length as s1.
38 *  @return  a binary string with same length as s1 and s2,
39 *   where each bit is the exclusive-or of the corresponding bits in s1-s2.
40 */
ex_or(lua_State * L)41 static int ex_or (lua_State *L) {
42   size_t l1, l2;
43   const char *s1 = luaL_checklstring(L, 1, &l1);
44   const char *s2 = luaL_checklstring(L, 2, &l2);
45   luaL_Buffer b;
46   luaL_argcheck( L, l1 == l2, 2, "lengths must be equal" );
47   luaL_buffinit(L, &b);
48   while (l1--) luaL_addchar(&b, (*s1++)^(*s2++));
49   luaL_pushresult(&b);
50   return 1;
51 }
52 
53 
checkseed(lua_State * L)54 static void checkseed (lua_State *L) {
55   if (lua_isnone(L, 3)) {  /* no seed? */
56     time_t tm = time(NULL);  /* for `random' seed */
57     lua_pushlstring(L, (char *)&tm, sizeof(tm));
58   }
59 }
60 
61 
62 #define MAXKEY	256
63 #define BLOCKSIZE	16
64 
65 
66 
initblock(lua_State * L,const char * seed,int lseed,char * block)67 static int initblock (lua_State *L, const char *seed, int lseed, char *block) {
68   size_t lkey;
69   const char *key = luaL_checklstring(L, 2, &lkey);
70   if (lkey > MAXKEY)
71     luaL_error(L, "key too long (> %d)", MAXKEY);
72   memset(block, 0, BLOCKSIZE);
73   memcpy(block, seed, lseed);
74   memcpy(block+BLOCKSIZE, key, lkey);
75   return (int)lkey+BLOCKSIZE;
76 }
77 
78 
codestream(lua_State * L,const char * msg,size_t lmsg,char * block,int lblock)79 static void codestream (lua_State *L, const char *msg, size_t lmsg,
80                                       char *block, int lblock) {
81   luaL_Buffer b;
82   luaL_buffinit(L, &b);
83   while (lmsg > 0) {
84     char code[BLOCKSIZE];
85     int i;
86     md5(block, lblock, code);
87     for (i=0; i<BLOCKSIZE && lmsg > 0; i++, lmsg--)
88       code[i] ^= *msg++;
89     luaL_addlstring(&b, code, i);
90     memcpy(block, code, i); /* update seed */
91   }
92   luaL_pushresult(&b);
93 }
94 
95 
decodestream(lua_State * L,const char * cypher,size_t lcypher,char * block,int lblock)96 static void decodestream (lua_State *L, const char *cypher, size_t lcypher,
97                           char *block, int lblock) {
98   luaL_Buffer b;
99   luaL_buffinit(L, &b);
100   while (lcypher > 0) {
101     char code[BLOCKSIZE];
102     int i;
103     md5(block, lblock, code);  /* update seed */
104     for (i=0; i<BLOCKSIZE && lcypher > 0; i++, lcypher--)
105       code[i] ^= *cypher++;
106     luaL_addlstring(&b, code, i);
107     memcpy(block, cypher-i, i);
108   }
109   luaL_pushresult(&b);
110 }
111 
112 
113 /**
114 *  Encrypts a string. Uses the hash function md5 in CFB (Cipher-feedback
115 *  mode).
116 *  @param message: arbitrary binary string to be encrypted.
117 *  @param key: arbitrary binary string to be used as a key.
118 *  @param [seed]: optional arbitrary binary string to be used as a seed.
119 *  if no seed is provided, the function uses the result of
120 *  <code>time()</code> as a seed.
121 *  @return  The cyphertext (as a binary string).
122 */
crypt(lua_State * L)123 static int crypt (lua_State *L) {
124   size_t lmsg;
125   const char *msg = luaL_checklstring(L, 1, &lmsg);
126   size_t lseed;
127   const char *seed;
128   int lblock;
129   char block[BLOCKSIZE+MAXKEY];
130   checkseed(L);
131   seed = luaL_checklstring(L, 3, &lseed);
132   if (lseed > BLOCKSIZE)
133     luaL_error(L, "seed too long (> %d)", BLOCKSIZE);
134   /* put seed and seed length at the beginning of result */
135   block[0] = (char)lseed;
136   memcpy(block+1, seed, lseed);
137   lua_pushlstring(L, block, lseed+1);  /* to concat with result */
138   lblock = initblock(L, seed, lseed, block);
139   codestream(L, msg, lmsg, block, lblock);
140   lua_concat(L, 2);
141   return 1;
142 }
143 
144 
145 /**
146 *  Decrypts a string. For any message, key, and seed, we have that
147 *  <code>decrypt(crypt(msg, key, seed), key) == msg</code>.
148 *  @param cyphertext: message to be decrypted (this must be the result of
149    a previous call to <code>crypt</code>.
150 *  @param key: arbitrary binary string to be used as a key.
151 *  @return  The plaintext.
152 */
decrypt(lua_State * L)153 static int decrypt (lua_State *L) {
154   size_t lcyphertext;
155   const char *cyphertext = luaL_checklstring(L, 1, &lcyphertext);
156   size_t lseed = cyphertext[0];
157   const char *seed = cyphertext+1;
158   int lblock;
159   char block[BLOCKSIZE+MAXKEY];
160   luaL_argcheck(L, lcyphertext >= lseed+1 && lseed <= BLOCKSIZE, 1,
161                  "invalid cyphered string");
162   cyphertext += lseed+1;
163   lcyphertext -= lseed+1;
164   lblock = initblock(L, seed, lseed, block);
165   decodestream(L, cyphertext, lcyphertext, block, lblock);
166   return 1;
167 }
168 
169 
170 static struct luaL_Reg md5lib[] = {
171   {"sum", lmd5},
172   {"exor", ex_or},
173   {"crypt", crypt},
174   {"decrypt", decrypt},
175   {NULL, NULL}
176 };
177 
178 
luaopen_md5(lua_State * L)179 int luaopen_md5 (lua_State *L) {
180   luaL_openlib(L, "md5", md5lib, 0);
181   return 1;
182 }
183 
184