author | Ryan C. Gordon <icculus@icculus.org> |
Tue, 17 Dec 2013 22:36:55 -0500 | |
changeset 1 | 0919d17b13f9 |
parent 0 | d7ee4e2ed49d |
child 2 | 16a2d269fd41 |
permissions | -rw-r--r-- |
0 | 1 |
#include <stdio.h> |
2 |
#include <stdlib.h> |
|
3 |
#include <string.h> |
|
4 |
#include <assert.h> |
|
5 |
#include "lua.h" |
|
6 |
#include "lauxlib.h" |
|
7 |
#include "lualib.h" |
|
8 |
#include "pkcs5_pbkdf2.h" |
|
9 |
#include "aes.h" |
|
10 |
#include "base64.h" |
|
11 |
#include "md5.h" |
|
12 |
||
13 |
#define STATICARRAYLEN(x) ( (sizeof ((x))) / (sizeof ((x)[0])) ) |
|
14 |
||
15 |
static lua_State *luaState = NULL; |
|
16 |
static const uint8_t zero16[16] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }; |
|
17 |
static const char saltprefix[] = { 'S', 'a', 'l', 't', 'e', 'd', '_', '_' }; |
|
18 |
||
19 |
static inline int retvalStringBytes(lua_State *L, const uint8_t *str, size_t len) |
|
20 |
{ |
|
21 |
if (str != NULL) |
|
22 |
lua_pushlstring(L, (const char *) str, len); |
|
23 |
else |
|
24 |
lua_pushnil(L); |
|
25 |
return 1; |
|
26 |
} // retvalStringBytes |
|
27 |
||
28 |
static inline void xorBlock(uint8_t *dst, const uint8_t *src) |
|
29 |
{ |
|
30 |
int i; |
|
31 |
for (i = 0; i < 16; i++, dst++, src++) |
|
32 |
*dst ^= *src; |
|
33 |
} // xorBlock |
|
34 |
||
35 |
static int decryptUsingKeyAndIvec(uint8_t *data, size_t *datalen, const uint8_t *key, const uint8_t *iv) |
|
36 |
{ |
|
37 |
const size_t blocks = *datalen / 16; |
|
38 |
uint8_t *block = data + ((blocks-1) * 16); // start at final block, work backwards |
|
39 |
const uint8_t *padding = &block[15]; |
|
40 |
uint8_t expkey[aesExpandedKeySize]; |
|
41 |
size_t i; |
|
42 |
||
43 |
if (blocks == 0) |
|
44 |
return 1; // nothing to do. |
|
45 |
||
46 |
aesExpandKey(key, expkey); |
|
47 |
||
48 |
for (i = 0; i < blocks-1; i++) |
|
49 |
{ |
|
50 |
aesDecrypt(block, expkey, block); // decrypt in place. |
|
51 |
xorBlock(block, block-16); |
|
52 |
block -= 16; |
|
53 |
} |
|
54 |
aesDecrypt(block, expkey, block); // decrypt in place. |
|
55 |
xorBlock(block, iv); // xor against initial vector for final block. |
|
56 |
||
57 |
if (*padding > 16) |
|
58 |
return 0; // bad data? |
|
59 |
||
60 |
*datalen -= *padding; |
|
61 |
||
62 |
return 1; |
|
63 |
} // decryptBinaryUsingKeyAndIvec |
|
64 |
||
65 |
||
66 |
static inline int isSalted(const uint8_t *data, const size_t datalen) |
|
67 |
{ |
|
68 |
return ( (datalen > sizeof (saltprefix)) && |
|
69 |
(memcmp(data, saltprefix, sizeof (saltprefix)) == 0) ); |
|
70 |
} // isSalted |
|
71 |
||
72 |
||
73 |
static int decryptUsingPBKDF2(lua_State *L) |
|
74 |
{ |
|
75 |
const char *base64 = luaL_checkstring(L, 1); |
|
76 |
const char *password = luaL_checkstring(L, 2); |
|
77 |
const int iterations = luaL_checkinteger(L, 3); |
|
78 |
size_t datalen = strlen(base64); |
|
79 |
uint8_t *dataptr = (uint8_t *) malloc(datalen); |
|
80 |
uint8_t *data = dataptr; |
|
81 |
base64_decodestate base64state; |
|
82 |
||
83 |
base64_init_decodestate(&base64state); |
|
84 |
datalen = base64_decode_block(base64, (int) datalen, data, &base64state); |
|
85 |
||
86 |
const uint8_t *salt = zero16; |
|
87 |
int saltlen = sizeof (zero16); |
|
88 |
if (isSalted(data, datalen)) |
|
89 |
{ |
|
90 |
salt = data + 8; |
|
91 |
saltlen = 8; |
|
92 |
data += 16; |
|
93 |
datalen -= 16; |
|
94 |
} // if |
|
95 |
||
96 |
uint8_t output[32]; |
|
97 |
pkcs5_pbkdf2(password, strlen(password), salt, saltlen, output, sizeof (output), (unsigned int) iterations); |
|
98 |
||
99 |
const uint8_t *aeskey = &output[0]; |
|
100 |
const uint8_t *aesiv = &output[16]; |
|
101 |
if (decryptUsingKeyAndIvec(data, &datalen, aeskey, aesiv)) |
|
102 |
retvalStringBytes(L, data, datalen); |
|
103 |
else |
|
104 |
lua_pushnil(L); |
|
105 |
||
106 |
free(dataptr); |
|
107 |
return 1; |
|
108 |
} // decryptUsingPBKDF2 |
|
109 |
||
110 |
||
111 |
static int decryptBase64UsingKey(lua_State *L) |
|
112 |
{ |
|
113 |
size_t keylen = 0; |
|
114 |
const char *base64 = luaL_checkstring(L, 1); |
|
115 |
const uint8_t *key = (const uint8_t *) luaL_checklstring(L, 2, &keylen); |
|
116 |
size_t datalen = strlen(base64); |
|
117 |
uint8_t *dataptr = (uint8_t *) malloc(datalen); |
|
118 |
uint8_t *data = dataptr; |
|
119 |
base64_decodestate base64state; |
|
120 |
||
121 |
base64_init_decodestate(&base64state); |
|
122 |
datalen = base64_decode_block(base64, (int) datalen, data, &base64state); |
|
123 |
||
124 |
uint8_t aeskey[16]; |
|
125 |
uint8_t aesiv[16]; |
|
126 |
MD5_CTX md5; |
|
127 |
||
128 |
if (isSalted(data, datalen)) |
|
129 |
{ |
|
130 |
const uint8_t *salt = data + 8; |
|
131 |
const size_t saltlen = 8; |
|
132 |
data += 16; |
|
133 |
datalen -= 16; |
|
134 |
||
135 |
assert(aesNr == 10); // AES-256 needs more rounds. |
|
136 |
assert(aesNk == 4); // hashing size is hardcoded later. |
|
137 |
uint8_t hashing[32]; |
|
138 |
||
139 |
MD5_init(&md5); |
|
140 |
MD5_append(&md5, key, keylen); |
|
141 |
MD5_append(&md5, salt, saltlen); |
|
142 |
MD5_finish(&md5, hashing); |
|
143 |
||
144 |
MD5_init(&md5); |
|
145 |
MD5_append(&md5, hashing, 16); |
|
146 |
MD5_append(&md5, key, keylen); |
|
147 |
MD5_append(&md5, salt, saltlen); |
|
148 |
MD5_finish(&md5, &hashing[16]); |
|
149 |
||
150 |
memcpy(aeskey, hashing, 4 * aesNk); |
|
151 |
memcpy(aesiv, &hashing[4 * aesNk], 16); |
|
152 |
} // if |
|
153 |
else |
|
154 |
{ |
|
155 |
MD5_init(&md5); |
|
156 |
MD5_append(&md5, key, keylen); |
|
157 |
MD5_finish(&md5, aeskey); |
|
158 |
memset(aesiv, '\0', sizeof (aesiv)); |
|
159 |
} // else |
|
160 |
||
161 |
if (decryptUsingKeyAndIvec(data, &datalen, aeskey, aesiv)) |
|
162 |
retvalStringBytes(L, data, datalen); |
|
163 |
else |
|
164 |
lua_pushnil(L); |
|
165 |
||
166 |
free(dataptr); |
|
167 |
return 1; |
|
168 |
} // decryptBase64UsingKey |
|
169 |
||
170 |
||
171 |
static void registerLuaLibs(lua_State *L) |
|
172 |
{ |
|
173 |
// We always need the string and base libraries (although base has a |
|
174 |
// few we could trim). The rest you can compile in if you want/need them. |
|
175 |
int i; |
|
176 |
static const luaL_Reg lualibs[] = { |
|
177 |
{"_G", luaopen_base}, |
|
178 |
{LUA_STRLIBNAME, luaopen_string}, |
|
179 |
{LUA_TABLIBNAME, luaopen_table}, |
|
180 |
{LUA_LOADLIBNAME, luaopen_package}, |
|
181 |
{LUA_IOLIBNAME, luaopen_io}, |
|
182 |
{LUA_OSLIBNAME, luaopen_os}, |
|
183 |
{LUA_MATHLIBNAME, luaopen_math}, |
|
184 |
{LUA_DBLIBNAME, luaopen_debug}, |
|
185 |
{LUA_BITLIBNAME, luaopen_bit32}, |
|
186 |
{LUA_COLIBNAME, luaopen_coroutine}, |
|
187 |
}; |
|
188 |
||
189 |
for (i = 0; i < STATICARRAYLEN(lualibs); i++) |
|
190 |
{ |
|
191 |
luaL_requiref(L, lualibs[i].name, lualibs[i].func, 1); |
|
192 |
lua_pop(L, 1); // remove lib |
|
193 |
} // for |
|
194 |
} // registerLuaLibs |
|
195 |
||
196 |
||
197 |
static void *luaAlloc(void *ud, void *ptr, size_t osize, size_t nsize) |
|
198 |
{ |
|
199 |
if (nsize == 0) |
|
200 |
{ |
|
201 |
free(ptr); |
|
202 |
return NULL; |
|
203 |
} // if |
|
204 |
return realloc(ptr, nsize); |
|
205 |
} // luaAlloc |
|
206 |
||
207 |
||
208 |
static inline void luaSetCFunc(lua_State *L, lua_CFunction f, const char *sym) |
|
209 |
{ |
|
210 |
lua_pushcfunction(L, f); |
|
211 |
lua_setglobal(luaState, sym); |
|
212 |
} // luaSetCFunc |
|
213 |
||
214 |
||
215 |
static int luaFatal(lua_State *L) |
|
216 |
{ |
|
217 |
const char *errstr = lua_tostring(L, -1); |
|
218 |
fprintf(stderr, "Lua panic: %s\n", errstr ? errstr : "(?)"); |
|
219 |
fflush(stderr); |
|
220 |
exit(1); |
|
221 |
} // luaFatal |
|
222 |
||
223 |
||
224 |
static int initLua(void) |
|
225 |
{ |
|
226 |
assert(luaState == NULL); |
|
227 |
luaState = lua_newstate(luaAlloc, NULL); |
|
228 |
||
229 |
lua_atpanic(luaState, luaFatal); |
|
230 |
assert(lua_checkstack(luaState, 20)); // Just in case. |
|
231 |
registerLuaLibs(luaState); |
|
232 |
||
233 |
// Set up initial C functions, etc we want to expose to Lua code... |
|
234 |
luaSetCFunc(luaState, decryptUsingPBKDF2, "decryptUsingPBKDF2"); |
|
235 |
luaSetCFunc(luaState, decryptBase64UsingKey, "decryptBase64UsingKey"); |
|
236 |
||
237 |
// Transfer control to Lua to setup some APIs and state... |
|
238 |
if (luaL_dofile(luaState, "1pass.lua") != 0) |
|
239 |
{ |
|
240 |
const char *msg = lua_tostring(luaState, -1); |
|
241 |
fprintf(stderr, "1pass.lua didn't run: %s\n", msg); |
|
242 |
lua_pop(luaState, 1); |
|
243 |
return 0; |
|
244 |
} // if |
|
245 |
||
246 |
return 1; |
|
247 |
} // initLua |
|
248 |
||
249 |
||
1
0919d17b13f9
Move the mainline into Lua.
Ryan C. Gordon <icculus@icculus.org>
parents:
0
diff
changeset
|
250 |
static void deinitLua(void) |
0 | 251 |
{ |
252 |
if (luaState != NULL) |
|
253 |
{ |
|
254 |
lua_close(luaState); |
|
255 |
luaState = NULL; |
|
256 |
} // if |
|
257 |
} // deinitLua |
|
258 |
||
259 |
||
260 |
int main(int argc, char **argv) |
|
261 |
{ |
|
1
0919d17b13f9
Move the mainline into Lua.
Ryan C. Gordon <icculus@icculus.org>
parents:
0
diff
changeset
|
262 |
atexit(deinitLua); |
0 | 263 |
|
1
0919d17b13f9
Move the mainline into Lua.
Ryan C. Gordon <icculus@icculus.org>
parents:
0
diff
changeset
|
264 |
if (!initLua()) // this will move control to 1pass.lua |
0 | 265 |
{ |
266 |
fprintf(stderr, "uhoh\n"); |
|
267 |
return 1; |
|
268 |
} // if |
|
269 |
||
270 |
return 0; |
|
271 |
} // main |
|
272 |
||
273 |
// end of 1pass.c ... |
|
274 |