1pass.c
author Ryan C. Gordon <icculus@icculus.org>
Mon, 10 Feb 2014 20:12:19 -0500
changeset 26 52d346bbaa51
parent 17 e884dbb403cc
child 28 60136c984e3b
permissions -rw-r--r--
Try to get the password window to the top of the z-order.
icculus@0
     1
#include <stdio.h>
icculus@0
     2
#include <stdlib.h>
icculus@0
     3
#include <string.h>
icculus@0
     4
#include <assert.h>
icculus@0
     5
#include "lua.h"
icculus@0
     6
#include "lauxlib.h"
icculus@0
     7
#include "lualib.h"
icculus@0
     8
#include "pkcs5_pbkdf2.h"
icculus@0
     9
#include "aes.h"
icculus@0
    10
#include "base64.h"
icculus@0
    11
#include "md5.h"
icculus@17
    12
#include "keyhook.h"
icculus@11
    13
#include <gtk/gtk.h>
icculus@0
    14
icculus@0
    15
#define STATICARRAYLEN(x) ( (sizeof ((x))) / (sizeof ((x)[0])) )
icculus@0
    16
icculus@0
    17
static lua_State *luaState = NULL;
icculus@0
    18
static const uint8_t zero16[16] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 };
icculus@0
    19
static const char saltprefix[] = { 'S', 'a', 'l', 't', 'e', 'd', '_', '_' };
icculus@0
    20
icculus@0
    21
static inline int retvalStringBytes(lua_State *L, const uint8_t *str, size_t len)
icculus@0
    22
{
icculus@0
    23
    if (str != NULL)
icculus@0
    24
        lua_pushlstring(L, (const char *) str, len);
icculus@0
    25
    else
icculus@0
    26
        lua_pushnil(L);
icculus@0
    27
    return 1;
icculus@0
    28
} // retvalStringBytes
icculus@0
    29
icculus@12
    30
static inline int retvalString(lua_State *L, const char *str)
icculus@12
    31
{
icculus@15
    32
    return retvalStringBytes(L, (const uint8_t *) str, str ? strlen(str) : 0);
icculus@12
    33
} // retvalString
icculus@12
    34
icculus@11
    35
static inline int retvalPointer(lua_State *L, void *ptr)
icculus@11
    36
{
icculus@11
    37
    if (ptr != NULL)
icculus@11
    38
        lua_pushlightuserdata(L, ptr);
icculus@11
    39
    else
icculus@11
    40
        lua_pushnil(L);
icculus@11
    41
    return 1;
icculus@11
    42
} // retvalPointer
icculus@11
    43
icculus@0
    44
static inline void xorBlock(uint8_t *dst, const uint8_t *src)
icculus@0
    45
{
icculus@0
    46
    int i;
icculus@0
    47
    for (i = 0; i < 16; i++, dst++, src++)
icculus@0
    48
        *dst ^= *src;
icculus@0
    49
} // xorBlock
icculus@0
    50
icculus@0
    51
static int decryptUsingKeyAndIvec(uint8_t *data, size_t *datalen, const uint8_t *key, const uint8_t *iv)
icculus@0
    52
{
icculus@0
    53
    const size_t blocks = *datalen / 16;
icculus@0
    54
    uint8_t *block = data + ((blocks-1) * 16);   // start at final block, work backwards
icculus@0
    55
    const uint8_t *padding = &block[15];
icculus@0
    56
    uint8_t expkey[aesExpandedKeySize];
icculus@0
    57
    size_t i;
icculus@0
    58
icculus@0
    59
    if (blocks == 0)
icculus@0
    60
        return 1;  // nothing to do.
icculus@0
    61
icculus@0
    62
	aesExpandKey(key, expkey);
icculus@0
    63
icculus@0
    64
    for (i = 0; i < blocks-1; i++)
icculus@0
    65
    {
icculus@0
    66
        aesDecrypt(block, expkey, block);   // decrypt in place.
icculus@0
    67
        xorBlock(block, block-16);
icculus@0
    68
        block -= 16;
icculus@0
    69
    }
icculus@0
    70
    aesDecrypt(block, expkey, block);   // decrypt in place.
icculus@0
    71
    xorBlock(block, iv);   // xor against initial vector for final block.
icculus@0
    72
icculus@0
    73
    if (*padding > 16)
icculus@0
    74
        return 0;  // bad data?
icculus@0
    75
icculus@0
    76
    *datalen -= *padding;
icculus@0
    77
icculus@0
    78
    return 1;
icculus@0
    79
} // decryptBinaryUsingKeyAndIvec
icculus@0
    80
icculus@0
    81
icculus@0
    82
static inline int isSalted(const uint8_t *data, const size_t datalen)
icculus@0
    83
{
icculus@0
    84
    return ( (datalen > sizeof (saltprefix)) &&
icculus@0
    85
             (memcmp(data, saltprefix, sizeof (saltprefix)) == 0) );
icculus@0
    86
} // isSalted
icculus@0
    87
icculus@0
    88
icculus@0
    89
static int decryptUsingPBKDF2(lua_State *L)
icculus@0
    90
{
icculus@0
    91
    const char *base64 = luaL_checkstring(L, 1);
icculus@0
    92
    const char *password = luaL_checkstring(L, 2);
icculus@0
    93
    const int iterations = luaL_checkinteger(L, 3);
icculus@0
    94
    size_t datalen = strlen(base64);
icculus@0
    95
    uint8_t *dataptr = (uint8_t *) malloc(datalen);
icculus@0
    96
    uint8_t *data = dataptr;
icculus@0
    97
    base64_decodestate base64state;
icculus@0
    98
icculus@0
    99
    base64_init_decodestate(&base64state);
icculus@0
   100
    datalen = base64_decode_block(base64, (int) datalen, data, &base64state);
icculus@0
   101
icculus@0
   102
    const uint8_t *salt = zero16;
icculus@0
   103
    int saltlen = sizeof (zero16);
icculus@0
   104
    if (isSalted(data, datalen))
icculus@0
   105
    {
icculus@0
   106
        salt = data + 8;
icculus@0
   107
        saltlen = 8;
icculus@0
   108
        data += 16;
icculus@0
   109
        datalen -= 16;
icculus@0
   110
    } // if
icculus@0
   111
icculus@0
   112
    uint8_t output[32];
icculus@0
   113
    pkcs5_pbkdf2(password, strlen(password), salt, saltlen, output, sizeof (output), (unsigned int) iterations);
icculus@0
   114
icculus@0
   115
    const uint8_t *aeskey = &output[0];
icculus@0
   116
    const uint8_t *aesiv = &output[16];
icculus@0
   117
	if (decryptUsingKeyAndIvec(data, &datalen, aeskey, aesiv))
icculus@0
   118
        retvalStringBytes(L, data, datalen);
icculus@0
   119
    else
icculus@0
   120
        lua_pushnil(L);
icculus@0
   121
icculus@0
   122
    free(dataptr);
icculus@0
   123
    return 1;
icculus@0
   124
} // decryptUsingPBKDF2
icculus@0
   125
icculus@0
   126
icculus@0
   127
static int decryptBase64UsingKey(lua_State *L)
icculus@0
   128
{
icculus@0
   129
    size_t keylen = 0;
icculus@0
   130
    const char *base64 = luaL_checkstring(L, 1);
icculus@0
   131
    const uint8_t *key = (const uint8_t *) luaL_checklstring(L, 2, &keylen);
icculus@0
   132
    size_t datalen = strlen(base64);
icculus@0
   133
    uint8_t *dataptr = (uint8_t *) malloc(datalen);
icculus@0
   134
    uint8_t *data = dataptr;
icculus@0
   135
    base64_decodestate base64state;
icculus@0
   136
icculus@0
   137
    base64_init_decodestate(&base64state);
icculus@0
   138
    datalen = base64_decode_block(base64, (int) datalen, data, &base64state);
icculus@0
   139
icculus@0
   140
    uint8_t aeskey[16];
icculus@0
   141
    uint8_t aesiv[16];
icculus@0
   142
    MD5_CTX md5;
icculus@0
   143
icculus@0
   144
    if (isSalted(data, datalen))
icculus@0
   145
    {
icculus@0
   146
        const uint8_t *salt = data + 8;
icculus@0
   147
        const size_t saltlen = 8;
icculus@0
   148
        data += 16;
icculus@0
   149
        datalen -= 16;
icculus@0
   150
icculus@0
   151
        assert(aesNr == 10);  // AES-256 needs more rounds.
icculus@0
   152
        assert(aesNk == 4);   // hashing size is hardcoded later.
icculus@0
   153
        uint8_t hashing[32];
icculus@0
   154
icculus@0
   155
        MD5_init(&md5);
icculus@0
   156
        MD5_append(&md5, key, keylen);
icculus@0
   157
        MD5_append(&md5, salt, saltlen);
icculus@0
   158
        MD5_finish(&md5, hashing);
icculus@0
   159
icculus@0
   160
        MD5_init(&md5);
icculus@0
   161
        MD5_append(&md5, hashing, 16);
icculus@0
   162
        MD5_append(&md5, key, keylen);
icculus@0
   163
        MD5_append(&md5, salt, saltlen);
icculus@0
   164
        MD5_finish(&md5, &hashing[16]);
icculus@0
   165
icculus@0
   166
        memcpy(aeskey, hashing, 4 * aesNk);
icculus@0
   167
        memcpy(aesiv, &hashing[4 * aesNk], 16);
icculus@0
   168
    } // if
icculus@0
   169
    else
icculus@0
   170
    {
icculus@0
   171
        MD5_init(&md5);
icculus@0
   172
        MD5_append(&md5, key, keylen);
icculus@0
   173
        MD5_finish(&md5, aeskey);
icculus@0
   174
        memset(aesiv, '\0', sizeof (aesiv));
icculus@0
   175
    } // else
icculus@0
   176
icculus@0
   177
	if (decryptUsingKeyAndIvec(data, &datalen, aeskey, aesiv))
icculus@0
   178
        retvalStringBytes(L, data, datalen);
icculus@0
   179
    else
icculus@0
   180
        lua_pushnil(L);
icculus@0
   181
icculus@0
   182
    free(dataptr);
icculus@0
   183
    return 1;
icculus@0
   184
} // decryptBase64UsingKey
icculus@0
   185
icculus@0
   186
icculus@12
   187
static int runGuiPasswordPrompt(lua_State *L)
icculus@12
   188
{
icculus@12
   189
    const char *hintstr = lua_tostring(L, 1);
icculus@12
   190
    GtkWidget *dialog = gtk_dialog_new_with_buttons(
icculus@12
   191
                            "Master Password", NULL, GTK_DIALOG_MODAL,
icculus@12
   192
                            GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
icculus@12
   193
                            GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
icculus@12
   194
                            NULL);
icculus@12
   195
icculus@12
   196
    GtkWidget *content_area = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
icculus@12
   197
icculus@12
   198
    if (hintstr != NULL)
icculus@12
   199
    {
icculus@12
   200
        GtkWidget *label = gtk_label_new(hintstr);
icculus@12
   201
        gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_CENTER);
icculus@12
   202
        gtk_container_add(GTK_CONTAINER(content_area), label);
icculus@12
   203
    } // if
icculus@12
   204
icculus@12
   205
    GtkWidget *entry = gtk_entry_new();
icculus@12
   206
    gtk_entry_set_visibility(GTK_ENTRY(entry), FALSE);
icculus@12
   207
    gtk_entry_set_activates_default(GTK_ENTRY(entry), TRUE);
icculus@12
   208
    gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT);
icculus@12
   209
    gtk_container_add(GTK_CONTAINER(content_area), entry);
icculus@12
   210
icculus@12
   211
    gtk_window_set_position(GTK_WINDOW(dialog), GTK_WIN_POS_MOUSE);
icculus@12
   212
    gtk_widget_show_all(dialog);
icculus@26
   213
    gtk_window_set_keep_above(GTK_WINDOW(dialog), TRUE);
icculus@12
   214
    const int ok = (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT);
icculus@12
   215
    retvalString(L, ok ? (const char *) gtk_entry_get_text(GTK_ENTRY(entry)) : NULL);
icculus@12
   216
    gtk_widget_destroy(dialog);
icculus@12
   217
icculus@12
   218
    return 1;
icculus@12
   219
} // runGuiPasswordPrompt
icculus@12
   220
icculus@12
   221
icculus@12
   222
static int copyToClipboard(lua_State *L)
icculus@12
   223
{
icculus@12
   224
    const char *str = luaL_checkstring(L, 1);
icculus@12
   225
    gtk_clipboard_set_text(gtk_clipboard_get(GDK_SELECTION_PRIMARY), str, -1);
icculus@12
   226
    gtk_clipboard_set_text(gtk_clipboard_get(GDK_SELECTION_CLIPBOARD), str, -1);
icculus@12
   227
} // copyToClipboard
icculus@12
   228
icculus@12
   229
icculus@11
   230
static int makeGuiMenu(lua_State *L)
icculus@11
   231
{
icculus@11
   232
    return retvalPointer(L, gtk_menu_new());
icculus@11
   233
} // makeGuiMenu
icculus@11
   234
icculus@11
   235
icculus@11
   236
static void clickedMenuItem(void *arg)
icculus@11
   237
{
icculus@11
   238
    // This is the callback from GTK+; now call into our actual Lua callback!
icculus@11
   239
    const int callback = (int) ((size_t)arg);
icculus@11
   240
    lua_rawgeti(luaState, LUA_REGISTRYINDEX, callback);
icculus@11
   241
    lua_call(luaState, 0, 0);
icculus@11
   242
} // clickedMenuItem
icculus@11
   243
icculus@12
   244
#if 0  // !!! FIXME: figure out how to fire this.
icculus@12
   245
static void deletedMenuItem(void *arg)
icculus@12
   246
{
icculus@12
   247
    // Clean up the Lua function we referenced in the Registry.
icculus@12
   248
    const int callback = (int) ((size_t)arg);
icculus@12
   249
printf("unref callback %d\n", callback);
icculus@12
   250
    luaL_unref(luaState, LUA_REGISTRYINDEX, callback);
icculus@12
   251
} // deletedMenuItem
icculus@12
   252
#endif
icculus@11
   253
icculus@11
   254
static int appendGuiMenuItem(lua_State *L)
icculus@11
   255
{
icculus@11
   256
    const int argc = lua_gettop(L);
icculus@11
   257
    GtkWidget *menu = (GtkWidget *) lua_touserdata(L, 1);
icculus@11
   258
    const char *label = luaL_checkstring(L, 2);
icculus@11
   259
    GtkWidget *item = gtk_menu_item_new_with_label(label);
icculus@11
   260
icculus@11
   261
    if ((argc >= 3) && (!lua_isnil(L, 3)))
icculus@11
   262
    {
icculus@11
   263
        assert(lua_isfunction(L, 3));
icculus@11
   264
        lua_pushvalue(L, 3);  // copy the Lua callback (luaL_ref() pops it).
icculus@11
   265
        const int callback = luaL_ref(L, LUA_REGISTRYINDEX);
icculus@11
   266
        gtk_signal_connect_object(GTK_OBJECT(item), "activate", GTK_SIGNAL_FUNC(clickedMenuItem), (gpointer) ((size_t)callback));
icculus@11
   267
    } // if
icculus@11
   268
icculus@11
   269
    gtk_widget_show(item);
icculus@11
   270
    gtk_menu_append(menu, item);
icculus@11
   271
    return retvalPointer(L, item);
icculus@11
   272
} // appendGuiMenuItem
icculus@11
   273
icculus@11
   274
icculus@11
   275
static int setGuiMenuItemSubmenu(lua_State *L)
icculus@11
   276
{
icculus@11
   277
    GtkMenuItem *item = (GtkMenuItem *) lua_touserdata(L, 1);
icculus@11
   278
    GtkWidget *submenu = (GtkWidget *) lua_touserdata(L, 2);
icculus@11
   279
    gtk_menu_item_set_submenu(item, submenu);
icculus@11
   280
    return 0;
icculus@11
   281
} // setGuiMenuItemSubmenu
icculus@11
   282
icculus@11
   283
icculus@11
   284
static int popupGuiMenu(lua_State *L)
icculus@11
   285
{
icculus@11
   286
    GtkMenu *menu = (GtkMenu *) lua_touserdata(L, 1);
icculus@11
   287
    gtk_menu_popup(menu, NULL, NULL, NULL, NULL, 0, gtk_get_current_event_time());
icculus@11
   288
    return 0;
icculus@11
   289
} // popupGuiMenu
icculus@11
   290
icculus@11
   291
icculus@17
   292
static void keyhookPressed(void)
icculus@17
   293
{
icculus@17
   294
    lua_getglobal(luaState, "keyhookPressed");
icculus@17
   295
    lua_call(luaState, 0, 0);
icculus@17
   296
} // keyhookPressed
icculus@17
   297
icculus@17
   298
icculus@17
   299
static gboolean keyhookPumper(void *arg)
icculus@17
   300
{
icculus@17
   301
    if (pumpKeyHook())
icculus@17
   302
        keyhookPressed();
icculus@17
   303
    return TRUE;  // keep firing timer
icculus@17
   304
} // keyhookPumper
icculus@17
   305
icculus@17
   306
icculus@11
   307
static int giveControlToGui(lua_State *L)
icculus@11
   308
{
icculus@17
   309
    if (initKeyHook())
icculus@17
   310
    {
icculus@17
   311
        atexit(deinitKeyHook);
icculus@17
   312
        g_timeout_add(200, (GSourceFunc) keyhookPumper, (gpointer) NULL);
icculus@17
   313
    } // if
icculus@17
   314
icculus@11
   315
    gtk_main();
icculus@11
   316
    return 0;
icculus@11
   317
} // giveControlToGui
icculus@11
   318
icculus@7
   319
icculus@0
   320
static void *luaAlloc(void *ud, void *ptr, size_t osize, size_t nsize)
icculus@0
   321
{
icculus@0
   322
    if (nsize == 0)
icculus@0
   323
    {
icculus@0
   324
        free(ptr);
icculus@0
   325
        return NULL;
icculus@0
   326
    } // if
icculus@0
   327
    return realloc(ptr, nsize);
icculus@0
   328
} // luaAlloc
icculus@0
   329
icculus@0
   330
icculus@0
   331
static inline void luaSetCFunc(lua_State *L, lua_CFunction f, const char *sym)
icculus@0
   332
{
icculus@0
   333
    lua_pushcfunction(L, f);
icculus@0
   334
    lua_setglobal(luaState, sym);
icculus@0
   335
} // luaSetCFunc
icculus@0
   336
icculus@0
   337
icculus@0
   338
static int luaFatal(lua_State *L)
icculus@0
   339
{
icculus@0
   340
    const char *errstr = lua_tostring(L, -1);
icculus@0
   341
    fprintf(stderr, "Lua panic: %s\n", errstr ? errstr : "(?)");
icculus@0
   342
    fflush(stderr);
icculus@0
   343
    exit(1);
icculus@0
   344
} // luaFatal
icculus@0
   345
icculus@0
   346
icculus@11
   347
static void deinitLua(void)
icculus@11
   348
{
icculus@11
   349
    if (luaState != NULL)
icculus@11
   350
    {
icculus@11
   351
        lua_close(luaState);
icculus@11
   352
        luaState = NULL;
icculus@11
   353
    } // if
icculus@11
   354
} // deinitLua
icculus@11
   355
icculus@11
   356
icculus@7
   357
static int initLua(const int argc, char **argv)
icculus@0
   358
{
icculus@11
   359
    atexit(deinitLua);
icculus@11
   360
icculus@0
   361
    assert(luaState == NULL);
icculus@0
   362
    luaState = lua_newstate(luaAlloc, NULL);
icculus@0
   363
icculus@0
   364
    lua_atpanic(luaState, luaFatal);
icculus@0
   365
    assert(lua_checkstack(luaState, 20));  // Just in case.
icculus@2
   366
    luaL_openlibs(luaState);
icculus@0
   367
icculus@0
   368
    // Set up initial C functions, etc we want to expose to Lua code...
icculus@0
   369
    luaSetCFunc(luaState, decryptUsingPBKDF2, "decryptUsingPBKDF2");
icculus@0
   370
    luaSetCFunc(luaState, decryptBase64UsingKey, "decryptBase64UsingKey");
icculus@11
   371
    luaSetCFunc(luaState, makeGuiMenu, "makeGuiMenu");
icculus@11
   372
    luaSetCFunc(luaState, appendGuiMenuItem, "appendGuiMenuItem");
icculus@11
   373
    luaSetCFunc(luaState, setGuiMenuItemSubmenu, "setGuiMenuItemSubmenu");
icculus@11
   374
    luaSetCFunc(luaState, popupGuiMenu, "popupGuiMenu");
icculus@11
   375
    luaSetCFunc(luaState, giveControlToGui, "giveControlToGui");
icculus@12
   376
    luaSetCFunc(luaState, runGuiPasswordPrompt, "runGuiPasswordPrompt");
icculus@12
   377
    luaSetCFunc(luaState, copyToClipboard, "copyToClipboard");
icculus@0
   378
icculus@7
   379
    // Set up argv table...
icculus@7
   380
    lua_newtable(luaState);
icculus@7
   381
    int i;
icculus@7
   382
    for (i = 0; i < argc; i++)
icculus@7
   383
    {
icculus@7
   384
        lua_pushinteger(luaState, i+1);
icculus@7
   385
        lua_pushstring(luaState, argv[i]);
icculus@7
   386
        lua_settable(luaState, -3);
icculus@7
   387
    } // for
icculus@7
   388
    lua_setglobal(luaState, "argv");
icculus@7
   389
icculus@3
   390
    // Transfer control to Lua...
icculus@0
   391
    if (luaL_dofile(luaState, "1pass.lua") != 0)
icculus@16
   392
        luaFatal(luaState);
icculus@0
   393
icculus@0
   394
    return 1;
icculus@0
   395
} // initLua
icculus@0
   396
icculus@0
   397
icculus@0
   398
int main(int argc, char **argv)
icculus@0
   399
{
icculus@11
   400
    gtk_init(&argc, &argv);
icculus@0
   401
icculus@7
   402
    if (!initLua(argc, argv))  // this will move control to 1pass.lua
icculus@0
   403
        return 1;
icculus@0
   404
icculus@0
   405
    return 0;
icculus@0
   406
} // main
icculus@0
   407
icculus@0
   408
// end of 1pass.c ...
icculus@0
   409