1pass.c
changeset 0 d7ee4e2ed49d
child 1 0919d17b13f9
equal deleted inserted replaced
-1:000000000000 0:d7ee4e2ed49d
       
     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 
       
    20 static inline int retvalString(lua_State *L, const char *str)
       
    21 {
       
    22     if (str != NULL)
       
    23         lua_pushstring(L, str);
       
    24     else
       
    25         lua_pushnil(L);
       
    26     return 1;
       
    27 } // retvalString
       
    28 
       
    29 
       
    30 static inline int retvalStringBytes(lua_State *L, const uint8_t *str, size_t len)
       
    31 {
       
    32 //size_t i; printf("{\n"); for (i = 0; i < len; i++) { printf(" 0x%X\n", (unsigned int) str[i]); } printf(" }\n\n");
       
    33 
       
    34     if (str != NULL)
       
    35         lua_pushlstring(L, (const char *) str, len);
       
    36     else
       
    37         lua_pushnil(L);
       
    38     return 1;
       
    39 } // retvalStringBytes
       
    40 
       
    41 static inline void xorBlock(uint8_t *dst, const uint8_t *src)
       
    42 {
       
    43     int i;
       
    44     for (i = 0; i < 16; i++, dst++, src++)
       
    45         *dst ^= *src;
       
    46 } // xorBlock
       
    47 
       
    48 static int decryptUsingKeyAndIvec(uint8_t *data, size_t *datalen, const uint8_t *key, const uint8_t *iv)
       
    49 {
       
    50     const size_t blocks = *datalen / 16;
       
    51     uint8_t *block = data + ((blocks-1) * 16);   // start at final block, work backwards
       
    52     const uint8_t *padding = &block[15];
       
    53     uint8_t expkey[aesExpandedKeySize];
       
    54     size_t i;
       
    55 
       
    56     if (blocks == 0)
       
    57         return 1;  // nothing to do.
       
    58 
       
    59 	aesExpandKey(key, expkey);
       
    60 
       
    61     for (i = 0; i < blocks-1; i++)
       
    62     {
       
    63         aesDecrypt(block, expkey, block);   // decrypt in place.
       
    64         xorBlock(block, block-16);
       
    65         block -= 16;
       
    66     }
       
    67     aesDecrypt(block, expkey, block);   // decrypt in place.
       
    68     xorBlock(block, iv);   // xor against initial vector for final block.
       
    69 
       
    70     if (*padding > 16)
       
    71         return 0;  // bad data?
       
    72 
       
    73     *datalen -= *padding;
       
    74 
       
    75     return 1;
       
    76 } // decryptBinaryUsingKeyAndIvec
       
    77 
       
    78 
       
    79 static inline int isSalted(const uint8_t *data, const size_t datalen)
       
    80 {
       
    81     return ( (datalen > sizeof (saltprefix)) &&
       
    82              (memcmp(data, saltprefix, sizeof (saltprefix)) == 0) );
       
    83 } // isSalted
       
    84 
       
    85 
       
    86 static int decryptUsingPBKDF2(lua_State *L)
       
    87 {
       
    88     const char *base64 = luaL_checkstring(L, 1);
       
    89     const char *password = luaL_checkstring(L, 2);
       
    90     const int iterations = luaL_checkinteger(L, 3);
       
    91     size_t datalen = strlen(base64);
       
    92     uint8_t *dataptr = (uint8_t *) malloc(datalen);
       
    93     uint8_t *data = dataptr;
       
    94     base64_decodestate base64state;
       
    95 
       
    96     base64_init_decodestate(&base64state);
       
    97     datalen = base64_decode_block(base64, (int) datalen, data, &base64state);
       
    98 
       
    99     const uint8_t *salt = zero16;
       
   100     int saltlen = sizeof (zero16);
       
   101     if (isSalted(data, datalen))
       
   102     {
       
   103         salt = data + 8;
       
   104         saltlen = 8;
       
   105         data += 16;
       
   106         datalen -= 16;
       
   107     } // if
       
   108 
       
   109     uint8_t output[32];
       
   110     pkcs5_pbkdf2(password, strlen(password), salt, saltlen, output, sizeof (output), (unsigned int) iterations);
       
   111 
       
   112     const uint8_t *aeskey = &output[0];
       
   113     const uint8_t *aesiv = &output[16];
       
   114 	if (decryptUsingKeyAndIvec(data, &datalen, aeskey, aesiv))
       
   115         retvalStringBytes(L, data, datalen);
       
   116     else
       
   117         lua_pushnil(L);
       
   118 
       
   119     free(dataptr);
       
   120     return 1;
       
   121 } // decryptUsingPBKDF2
       
   122 
       
   123 
       
   124 static int decryptBase64UsingKey(lua_State *L)
       
   125 {
       
   126     size_t keylen = 0;
       
   127     const char *base64 = luaL_checkstring(L, 1);
       
   128     const uint8_t *key = (const uint8_t *) luaL_checklstring(L, 2, &keylen);
       
   129     size_t datalen = strlen(base64);
       
   130     uint8_t *dataptr = (uint8_t *) malloc(datalen);
       
   131     uint8_t *data = dataptr;
       
   132     base64_decodestate base64state;
       
   133 
       
   134     base64_init_decodestate(&base64state);
       
   135     datalen = base64_decode_block(base64, (int) datalen, data, &base64state);
       
   136 
       
   137     uint8_t aeskey[16];
       
   138     uint8_t aesiv[16];
       
   139     MD5_CTX md5;
       
   140 
       
   141     if (isSalted(data, datalen))
       
   142     {
       
   143         const uint8_t *salt = data + 8;
       
   144         const size_t saltlen = 8;
       
   145         data += 16;
       
   146         datalen -= 16;
       
   147 
       
   148         assert(aesNr == 10);  // AES-256 needs more rounds.
       
   149         assert(aesNk == 4);   // hashing size is hardcoded later.
       
   150         uint8_t hashing[32];
       
   151 
       
   152         MD5_init(&md5);
       
   153         MD5_append(&md5, key, keylen);
       
   154         MD5_append(&md5, salt, saltlen);
       
   155         MD5_finish(&md5, hashing);
       
   156 
       
   157         MD5_init(&md5);
       
   158         MD5_append(&md5, hashing, 16);
       
   159         MD5_append(&md5, key, keylen);
       
   160         MD5_append(&md5, salt, saltlen);
       
   161         MD5_finish(&md5, &hashing[16]);
       
   162 
       
   163         memcpy(aeskey, hashing, 4 * aesNk);
       
   164         memcpy(aesiv, &hashing[4 * aesNk], 16);
       
   165     } // if
       
   166     else
       
   167     {
       
   168         MD5_init(&md5);
       
   169         MD5_append(&md5, key, keylen);
       
   170         MD5_finish(&md5, aeskey);
       
   171         memset(aesiv, '\0', sizeof (aesiv));
       
   172     } // else
       
   173 
       
   174 	if (decryptUsingKeyAndIvec(data, &datalen, aeskey, aesiv))
       
   175         retvalStringBytes(L, data, datalen);
       
   176     else
       
   177         lua_pushnil(L);
       
   178 
       
   179     free(dataptr);
       
   180     return 1;
       
   181 } // decryptBase64UsingKey
       
   182 
       
   183 
       
   184 static void registerLuaLibs(lua_State *L)
       
   185 {
       
   186     // We always need the string and base libraries (although base has a
       
   187     //  few we could trim). The rest you can compile in if you want/need them.
       
   188     int i;
       
   189     static const luaL_Reg lualibs[] = {
       
   190         {"_G", luaopen_base},
       
   191         {LUA_STRLIBNAME, luaopen_string},
       
   192         {LUA_TABLIBNAME, luaopen_table},
       
   193         {LUA_LOADLIBNAME, luaopen_package},
       
   194         {LUA_IOLIBNAME, luaopen_io},
       
   195         {LUA_OSLIBNAME, luaopen_os},
       
   196         {LUA_MATHLIBNAME, luaopen_math},
       
   197         {LUA_DBLIBNAME, luaopen_debug},
       
   198         {LUA_BITLIBNAME, luaopen_bit32},
       
   199         {LUA_COLIBNAME, luaopen_coroutine},
       
   200     };
       
   201 
       
   202     for (i = 0; i < STATICARRAYLEN(lualibs); i++)
       
   203     {
       
   204         luaL_requiref(L, lualibs[i].name, lualibs[i].func, 1);
       
   205         lua_pop(L, 1);  // remove lib
       
   206     } // for
       
   207 } // registerLuaLibs
       
   208 
       
   209 
       
   210 static void *luaAlloc(void *ud, void *ptr, size_t osize, size_t nsize)
       
   211 {
       
   212     if (nsize == 0)
       
   213     {
       
   214         free(ptr);
       
   215         return NULL;
       
   216     } // if
       
   217     return realloc(ptr, nsize);
       
   218 } // luaAlloc
       
   219 
       
   220 
       
   221 static inline void luaSetCFunc(lua_State *L, lua_CFunction f, const char *sym)
       
   222 {
       
   223     lua_pushcfunction(L, f);
       
   224     lua_setglobal(luaState, sym);
       
   225 } // luaSetCFunc
       
   226 
       
   227 
       
   228 static int luaFatal(lua_State *L)
       
   229 {
       
   230     const char *errstr = lua_tostring(L, -1);
       
   231     fprintf(stderr, "Lua panic: %s\n", errstr ? errstr : "(?)");
       
   232     fflush(stderr);
       
   233     exit(1);
       
   234 } // luaFatal
       
   235 
       
   236 
       
   237 static int initLua(void)
       
   238 {
       
   239     assert(luaState == NULL);
       
   240     luaState = lua_newstate(luaAlloc, NULL);
       
   241 
       
   242     lua_atpanic(luaState, luaFatal);
       
   243     assert(lua_checkstack(luaState, 20));  // Just in case.
       
   244     registerLuaLibs(luaState);
       
   245 
       
   246     // Set up initial C functions, etc we want to expose to Lua code...
       
   247     luaSetCFunc(luaState, decryptUsingPBKDF2, "decryptUsingPBKDF2");
       
   248     luaSetCFunc(luaState, decryptBase64UsingKey, "decryptBase64UsingKey");
       
   249 
       
   250     // Transfer control to Lua to setup some APIs and state...
       
   251     if (luaL_dofile(luaState, "1pass.lua") != 0)
       
   252     {
       
   253         const char *msg = lua_tostring(luaState, -1);
       
   254         fprintf(stderr, "1pass.lua didn't run: %s\n", msg);
       
   255         lua_pop(luaState, 1);
       
   256         return 0;
       
   257     } // if
       
   258 
       
   259     return 1;
       
   260 } // initLua
       
   261 
       
   262 
       
   263 void deinitLua(void)
       
   264 {
       
   265     if (luaState != NULL)
       
   266     {
       
   267         lua_close(luaState);
       
   268         luaState = NULL;
       
   269     } // if
       
   270 } // deinitLua
       
   271 
       
   272 
       
   273 
       
   274 static char *loadKey(const char *baseDir, const char *level, const char *password)
       
   275 {
       
   276     char *retval = NULL;
       
   277     lua_getglobal(luaState, "loadKey");
       
   278     lua_pushstring(luaState, baseDir);
       
   279     lua_pushstring(luaState, level);
       
   280     lua_pushstring(luaState, password);
       
   281     lua_call(luaState, 3, 1);
       
   282     const char *str = lua_tostring(luaState, -1);
       
   283     if (str)
       
   284         retval = strdup(str);
       
   285     lua_pop(luaState, 1);
       
   286     return retval;
       
   287 } // luafunc_loadKey
       
   288 
       
   289 
       
   290 static char *sl5 = NULL;
       
   291 
       
   292 int main(int argc, char **argv)
       
   293 {
       
   294     const char *basedir = "1Password/1Password.agilekeychain/data/default";  // !!! FIXME
       
   295 
       
   296     char *password = NULL;
       
   297     size_t pwlen = 0;
       
   298     printf("password: "); fflush(stdout);
       
   299     const ssize_t rc = getline(&password, &pwlen, stdin);
       
   300     if (rc == -1)
       
   301         return 1;
       
   302     else if (password[rc-1] == '\n')
       
   303         password[rc-1] = 0;
       
   304 
       
   305     if (!initLua())
       
   306     {
       
   307         fprintf(stderr, "uhoh\n");
       
   308         return 1;
       
   309     } // if
       
   310 
       
   311     sl5 = loadKey(basedir, "SL5", password);
       
   312     if (!sl5)
       
   313     {
       
   314         fprintf(stderr, "wrong password?\n");
       
   315         return 1;
       
   316     } // if
       
   317 
       
   318     free(sl5);
       
   319     return 0;
       
   320 } // main
       
   321 
       
   322 // end of 1pass.c ...
       
   323