#include #include #include #include #include "lua.h" #include "lauxlib.h" #include "lualib.h" #include "pkcs5_pbkdf2.h" #include "aes.h" #include "base64.h" #include "md5.h" #include #define STATICARRAYLEN(x) ( (sizeof ((x))) / (sizeof ((x)[0])) ) static lua_State *luaState = NULL; static const uint8_t zero16[16] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }; static const char saltprefix[] = { 'S', 'a', 'l', 't', 'e', 'd', '_', '_' }; static inline int retvalStringBytes(lua_State *L, const uint8_t *str, size_t len) { if (str != NULL) lua_pushlstring(L, (const char *) str, len); else lua_pushnil(L); return 1; } // retvalStringBytes static inline int retvalString(lua_State *L, const char *str) { return retvalStringBytes(L, (const uint8_t *) str, str ? strlen(str) : 0); } // retvalString static inline int retvalPointer(lua_State *L, void *ptr) { if (ptr != NULL) lua_pushlightuserdata(L, ptr); else lua_pushnil(L); return 1; } // retvalPointer static inline void xorBlock(uint8_t *dst, const uint8_t *src) { int i; for (i = 0; i < 16; i++, dst++, src++) *dst ^= *src; } // xorBlock static int decryptUsingKeyAndIvec(uint8_t *data, size_t *datalen, const uint8_t *key, const uint8_t *iv) { const size_t blocks = *datalen / 16; uint8_t *block = data + ((blocks-1) * 16); // start at final block, work backwards const uint8_t *padding = &block[15]; uint8_t expkey[aesExpandedKeySize]; size_t i; if (blocks == 0) return 1; // nothing to do. aesExpandKey(key, expkey); for (i = 0; i < blocks-1; i++) { aesDecrypt(block, expkey, block); // decrypt in place. xorBlock(block, block-16); block -= 16; } aesDecrypt(block, expkey, block); // decrypt in place. xorBlock(block, iv); // xor against initial vector for final block. if (*padding > 16) return 0; // bad data? *datalen -= *padding; return 1; } // decryptBinaryUsingKeyAndIvec static inline int isSalted(const uint8_t *data, const size_t datalen) { return ( (datalen > sizeof (saltprefix)) && (memcmp(data, saltprefix, sizeof (saltprefix)) == 0) ); } // isSalted static int decryptUsingPBKDF2(lua_State *L) { const char *base64 = luaL_checkstring(L, 1); const char *password = luaL_checkstring(L, 2); const int iterations = luaL_checkinteger(L, 3); size_t datalen = strlen(base64); uint8_t *dataptr = (uint8_t *) malloc(datalen); uint8_t *data = dataptr; base64_decodestate base64state; base64_init_decodestate(&base64state); datalen = base64_decode_block(base64, (int) datalen, data, &base64state); const uint8_t *salt = zero16; int saltlen = sizeof (zero16); if (isSalted(data, datalen)) { salt = data + 8; saltlen = 8; data += 16; datalen -= 16; } // if uint8_t output[32]; pkcs5_pbkdf2(password, strlen(password), salt, saltlen, output, sizeof (output), (unsigned int) iterations); const uint8_t *aeskey = &output[0]; const uint8_t *aesiv = &output[16]; if (decryptUsingKeyAndIvec(data, &datalen, aeskey, aesiv)) retvalStringBytes(L, data, datalen); else lua_pushnil(L); free(dataptr); return 1; } // decryptUsingPBKDF2 static int decryptBase64UsingKey(lua_State *L) { size_t keylen = 0; const char *base64 = luaL_checkstring(L, 1); const uint8_t *key = (const uint8_t *) luaL_checklstring(L, 2, &keylen); size_t datalen = strlen(base64); uint8_t *dataptr = (uint8_t *) malloc(datalen); uint8_t *data = dataptr; base64_decodestate base64state; base64_init_decodestate(&base64state); datalen = base64_decode_block(base64, (int) datalen, data, &base64state); uint8_t aeskey[16]; uint8_t aesiv[16]; MD5_CTX md5; if (isSalted(data, datalen)) { const uint8_t *salt = data + 8; const size_t saltlen = 8; data += 16; datalen -= 16; assert(aesNr == 10); // AES-256 needs more rounds. assert(aesNk == 4); // hashing size is hardcoded later. uint8_t hashing[32]; MD5_init(&md5); MD5_append(&md5, key, keylen); MD5_append(&md5, salt, saltlen); MD5_finish(&md5, hashing); MD5_init(&md5); MD5_append(&md5, hashing, 16); MD5_append(&md5, key, keylen); MD5_append(&md5, salt, saltlen); MD5_finish(&md5, &hashing[16]); memcpy(aeskey, hashing, 4 * aesNk); memcpy(aesiv, &hashing[4 * aesNk], 16); } // if else { MD5_init(&md5); MD5_append(&md5, key, keylen); MD5_finish(&md5, aeskey); memset(aesiv, '\0', sizeof (aesiv)); } // else if (decryptUsingKeyAndIvec(data, &datalen, aeskey, aesiv)) retvalStringBytes(L, data, datalen); else lua_pushnil(L); free(dataptr); return 1; } // decryptBase64UsingKey static int runGuiPasswordPrompt(lua_State *L) { const char *hintstr = lua_tostring(L, 1); GtkWidget *dialog = gtk_dialog_new_with_buttons( "Master Password", NULL, GTK_DIALOG_MODAL, GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, NULL); GtkWidget *content_area = gtk_dialog_get_content_area(GTK_DIALOG(dialog)); if (hintstr != NULL) { GtkWidget *label = gtk_label_new(hintstr); gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_CENTER); gtk_container_add(GTK_CONTAINER(content_area), label); } // if GtkWidget *entry = gtk_entry_new(); gtk_entry_set_visibility(GTK_ENTRY(entry), FALSE); gtk_entry_set_activates_default(GTK_ENTRY(entry), TRUE); gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT); gtk_container_add(GTK_CONTAINER(content_area), entry); gtk_window_set_position(GTK_WINDOW(dialog), GTK_WIN_POS_MOUSE); gtk_widget_show_all(dialog); const int ok = (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT); retvalString(L, ok ? (const char *) gtk_entry_get_text(GTK_ENTRY(entry)) : NULL); gtk_widget_destroy(dialog); return 1; } // runGuiPasswordPrompt static int copyToClipboard(lua_State *L) { const char *str = luaL_checkstring(L, 1); gtk_clipboard_set_text(gtk_clipboard_get(GDK_SELECTION_PRIMARY), str, -1); gtk_clipboard_set_text(gtk_clipboard_get(GDK_SELECTION_CLIPBOARD), str, -1); } // copyToClipboard static int makeGuiMenu(lua_State *L) { return retvalPointer(L, gtk_menu_new()); } // makeGuiMenu static void clickedMenuItem(void *arg) { // This is the callback from GTK+; now call into our actual Lua callback! const int callback = (int) ((size_t)arg); lua_rawgeti(luaState, LUA_REGISTRYINDEX, callback); lua_call(luaState, 0, 0); } // clickedMenuItem #if 0 // !!! FIXME: figure out how to fire this. static void deletedMenuItem(void *arg) { // Clean up the Lua function we referenced in the Registry. const int callback = (int) ((size_t)arg); printf("unref callback %d\n", callback); luaL_unref(luaState, LUA_REGISTRYINDEX, callback); } // deletedMenuItem #endif static int appendGuiMenuItem(lua_State *L) { const int argc = lua_gettop(L); GtkWidget *menu = (GtkWidget *) lua_touserdata(L, 1); const char *label = luaL_checkstring(L, 2); GtkWidget *item = gtk_menu_item_new_with_label(label); if ((argc >= 3) && (!lua_isnil(L, 3))) { assert(lua_isfunction(L, 3)); lua_pushvalue(L, 3); // copy the Lua callback (luaL_ref() pops it). const int callback = luaL_ref(L, LUA_REGISTRYINDEX); gtk_signal_connect_object(GTK_OBJECT(item), "activate", GTK_SIGNAL_FUNC(clickedMenuItem), (gpointer) ((size_t)callback)); } // if gtk_widget_show(item); gtk_menu_append(menu, item); return retvalPointer(L, item); } // appendGuiMenuItem static int setGuiMenuItemSubmenu(lua_State *L) { GtkMenuItem *item = (GtkMenuItem *) lua_touserdata(L, 1); GtkWidget *submenu = (GtkWidget *) lua_touserdata(L, 2); gtk_menu_item_set_submenu(item, submenu); return 0; } // setGuiMenuItemSubmenu static int popupGuiMenu(lua_State *L) { GtkMenu *menu = (GtkMenu *) lua_touserdata(L, 1); gtk_menu_popup(menu, NULL, NULL, NULL, NULL, 0, gtk_get_current_event_time()); return 0; } // popupGuiMenu static int giveControlToGui(lua_State *L) { gtk_main(); return 0; } // giveControlToGui static void *luaAlloc(void *ud, void *ptr, size_t osize, size_t nsize) { if (nsize == 0) { free(ptr); return NULL; } // if return realloc(ptr, nsize); } // luaAlloc static inline void luaSetCFunc(lua_State *L, lua_CFunction f, const char *sym) { lua_pushcfunction(L, f); lua_setglobal(luaState, sym); } // luaSetCFunc static int luaFatal(lua_State *L) { const char *errstr = lua_tostring(L, -1); fprintf(stderr, "Lua panic: %s\n", errstr ? errstr : "(?)"); fflush(stderr); exit(1); } // luaFatal static void deinitLua(void) { if (luaState != NULL) { lua_close(luaState); luaState = NULL; } // if } // deinitLua static int initLua(const int argc, char **argv) { atexit(deinitLua); assert(luaState == NULL); luaState = lua_newstate(luaAlloc, NULL); lua_atpanic(luaState, luaFatal); assert(lua_checkstack(luaState, 20)); // Just in case. luaL_openlibs(luaState); // Set up initial C functions, etc we want to expose to Lua code... luaSetCFunc(luaState, decryptUsingPBKDF2, "decryptUsingPBKDF2"); luaSetCFunc(luaState, decryptBase64UsingKey, "decryptBase64UsingKey"); luaSetCFunc(luaState, makeGuiMenu, "makeGuiMenu"); luaSetCFunc(luaState, appendGuiMenuItem, "appendGuiMenuItem"); luaSetCFunc(luaState, setGuiMenuItemSubmenu, "setGuiMenuItemSubmenu"); luaSetCFunc(luaState, popupGuiMenu, "popupGuiMenu"); luaSetCFunc(luaState, giveControlToGui, "giveControlToGui"); luaSetCFunc(luaState, runGuiPasswordPrompt, "runGuiPasswordPrompt"); luaSetCFunc(luaState, copyToClipboard, "copyToClipboard"); // Set up argv table... lua_newtable(luaState); int i; for (i = 0; i < argc; i++) { lua_pushinteger(luaState, i+1); lua_pushstring(luaState, argv[i]); lua_settable(luaState, -3); } // for lua_setglobal(luaState, "argv"); // Transfer control to Lua... if (luaL_dofile(luaState, "1pass.lua") != 0) luaFatal(luaState); return 1; } // initLua int main(int argc, char **argv) { gtk_init(&argc, &argv); if (!initLua(argc, argv)) // this will move control to 1pass.lua return 1; return 0; } // main // end of 1pass.c ...