1pass.lua
author Ryan C. Gordon <icculus@icculus.org>
Thu, 19 Dec 2013 23:04:04 -0500
changeset 10 85496558f3a4
parent 9 98c872cc0560
child 11 b52e0f1798b8
permissions -rw-r--r--
Patched to compile on Linux.
icculus@0
     1
JSON = (loadfile "JSON.lua")()
icculus@0
     2
icculus@6
     3
local function load_json_str(str, desc)
icculus@6
     4
    local retval = JSON:decode(str)
icculus@6
     5
    return retval
icculus@6
     6
end
icculus@6
     7
icculus@0
     8
local function load_json(fname)
icculus@0
     9
    local f = io.open(fname, "rb")
icculus@0
    10
    if (f == nil) then
icculus@0
    11
        return nil
icculus@0
    12
    end
icculus@0
    13
icculus@0
    14
    local str = f:read("*all")
icculus@0
    15
    f:close()
icculus@0
    16
icculus@6
    17
    return load_json_str(str, fname)
icculus@0
    18
end
icculus@0
    19
icculus@0
    20
icculus@5
    21
local keys = {}
icculus@0
    22
function loadKey(basedir, level, password)
icculus@5
    23
    if keys[level] ~= nil then
icculus@5
    24
        return keys[level]
icculus@5
    25
    end
icculus@5
    26
icculus@0
    27
    local keysjson = load_json(basedir .. "/encryptionKeys.js");
icculus@0
    28
    if (keysjson == nil) or (keysjson[level] == nil) then
icculus@0
    29
        return nil
icculus@0
    30
    end
icculus@0
    31
icculus@0
    32
    local identifier = keysjson[level]
icculus@0
    33
    for i,v in ipairs(keysjson.list) do
icculus@0
    34
        if v.identifier == identifier then
icculus@0
    35
			local iterations = v.iterations
icculus@0
    36
            if (iterations == nil) or (iterations < 1000) then
icculus@0
    37
			    iterations = 1000
icculus@0
    38
            end
icculus@0
    39
icculus@0
    40
			local decrypted = decryptUsingPBKDF2(v.data, password, iterations)
icculus@0
    41
			if decrypted == nil then
icculus@0
    42
                return nil
icculus@0
    43
            end
icculus@0
    44
icculus@0
    45
			local validate = decryptBase64UsingKey(v.validation, decrypted)
icculus@0
    46
			if validate ~= decrypted then
icculus@0
    47
                return nil
icculus@0
    48
            end
icculus@0
    49
icculus@5
    50
            keys[level] = decrypted
icculus@0
    51
            return decrypted
icculus@0
    52
        end
icculus@0
    53
    end
icculus@0
    54
icculus@0
    55
    return nil
icculus@0
    56
end
icculus@0
    57
icculus@1
    58
local function showHint(basedir)
icculus@0
    59
    local f = io.open(basedir .. "/.password.hint", "r")
icculus@0
    60
    if (f == nil) then
icculus@0
    61
        return
icculus@0
    62
    end
icculus@0
    63
icculus@0
    64
    local str = f:read("*all")
icculus@0
    65
    f:close()
icculus@0
    66
icculus@0
    67
    print("(hint is '" .. str .. "').")
icculus@0
    68
end
icculus@0
    69
icculus@1
    70
icculus@6
    71
function loadContents(basedir)
icculus@6
    72
    return load_json(basedir .. "/contents.js");
icculus@6
    73
end
icculus@6
    74
icculus@8
    75
local function shouldFilterOut(filter, type, name, url)
icculus@8
    76
    if filter == nil then
icculus@8
    77
        return false   -- no filter? Don't filter.
icculus@8
    78
    elseif type == "system.Tombstone" then
icculus@8
    79
        return true    -- I guess those are dead items?
icculus@8
    80
    elseif string.find(string.lower(name), filter) ~= nil then
icculus@8
    81
        return false   -- matched keep-filter on name
icculus@8
    82
    elseif string.find(string.lower(url), filter) ~= nil then
icculus@8
    83
        return false   -- matched keep-filter on URL
icculus@8
    84
    end
icculus@8
    85
    return true  -- didn't match our keep-filter. Chuck it.
icculus@8
    86
end
icculus@8
    87
icculus@6
    88
icculus@1
    89
-- Mainline!
icculus@1
    90
icculus@7
    91
--for i,v in ipairs(argv) do
icculus@7
    92
--    print("argv[" .. i .. "] = " .. v)
icculus@7
    93
--end
icculus@7
    94
icculus@1
    95
local basedir = "1Password/1Password.agilekeychain/data/default"  -- !!! FIXME
icculus@1
    96
icculus@7
    97
local password = argv[3]
icculus@7
    98
if password == nil then
icculus@7
    99
    showHint(basedir)
icculus@7
   100
    io.write("password: ")
icculus@7
   101
    password = io.read("*l")
icculus@7
   102
end
icculus@1
   103
icculus@5
   104
if loadKey(basedir, "SL5", password) == nil then
icculus@1
   105
    print("wrong password?\n")
icculus@1
   106
    os.exit(1)
icculus@1
   107
end
icculus@1
   108
icculus@8
   109
local filter = argv[2]
icculus@8
   110
if filter ~= nil then
icculus@8
   111
    filter = string.lower(filter)
icculus@8
   112
end
icculus@8
   113
icculus@6
   114
items = loadContents(basedir)
icculus@6
   115
for i,v in ipairs(items) do
icculus@8
   116
    local type = v[2]
icculus@8
   117
    local name = v[3]
icculus@8
   118
    local url = v[4]
icculus@8
   119
    if not shouldFilterOut(filter, type, name, url) then
icculus@6
   120
        local metadata = load_json(basedir .. "/" .. v[1] .. ".1password")
icculus@6
   121
        if metadata ~= nil then
icculus@6
   122
            local plaintext = decryptBase64UsingKey(metadata.encrypted, loadKey(basedir, metadata.securityLevel, password))
icculus@8
   123
            local username = nil
icculus@8
   124
            local password = nil
icculus@6
   125
            if plaintext ~= nil then
icculus@6
   126
                local secure = load_json_str(plaintext, v[1])
icculus@8
   127
                if type == "webforms.WebForm" then
icculus@8
   128
                    for ii,vv in ipairs(secure.fields) do
icculus@8
   129
                        if vv.type == "P" then
icculus@8
   130
                            password = vv.value
icculus@8
   131
                        elseif vv.type == "E" then
icculus@8
   132
                            username = vv.value
icculus@8
   133
                        end
icculus@8
   134
                    end
icculus@8
   135
                elseif type == "passwords.Password" then
icculus@8
   136
                    password = secure.password
icculus@8
   137
                end
icculus@6
   138
            end
icculus@8
   139
icculus@8
   140
            print("item: " .. metadata.title)
icculus@8
   141
            if username ~= nil then print("username: " .. username) end
icculus@8
   142
            if password ~= nil then print("password: " .. password) end
icculus@8
   143
icculus@6
   144
        end
icculus@6
   145
    end
icculus@6
   146
end
icculus@1
   147
icculus@0
   148
-- end of 1pass.lua ...
icculus@0
   149