1pass.lua
changeset 12 6a2d5b34d5ca
parent 11 b52e0f1798b8
child 17 e884dbb403cc
equal deleted inserted replaced
11:b52e0f1798b8 12:6a2d5b34d5ca
     1 JSON = (loadfile "JSON.lua")()
     1 JSON = (loadfile "JSON.lua")()
       
     2 dofile("dumptable.lua")
       
     3 
       
     4 local passwordTypeNameMap = {
       
     5     ["webforms.WebForm"] = "Logins",
       
     6     ["wallet.financial.CreditCard"] = "Credit cards",
       
     7     ["passwords.Password"] = "Passwords",
       
     8     ["wallet.financial.BankAccountUS"] = "Bank accounts",
       
     9     ["wallet.membership.Membership"] = "Memberships",
       
    10     ["wallet.government.DriversLicense"] = "Drivers licenses",
       
    11     ["system.Tombstone"] = "Dead items",
       
    12     -- !!! FIXME: more!
       
    13 }
       
    14 
       
    15 local passwordTypeOrdering = {
       
    16     "webforms.WebForm",
       
    17     "wallet.financial.CreditCard",
       
    18     "passwords.Password",
       
    19     "wallet.financial.BankAccountUS",
       
    20     "wallet.membership.Membership",
       
    21     "wallet.government.DriversLicense",
       
    22     -- never show "system.Tombstone",
       
    23     -- !!! FIXME: more!
       
    24 }
     2 
    25 
     3 local function load_json_str(str, desc)
    26 local function load_json_str(str, desc)
     4     local retval = JSON:decode(str)
    27     local retval = JSON:decode(str)
     5     return retval
    28     return retval
     6 end
    29 end
    22 function loadKey(basedir, level, password)
    45 function loadKey(basedir, level, password)
    23     if keys[level] ~= nil then
    46     if keys[level] ~= nil then
    24         return keys[level]
    47         return keys[level]
    25     end
    48     end
    26 
    49 
    27     local keysjson = load_json(basedir .. "/encryptionKeys.js");
    50     local keysjson = load_json(basedir .. "/encryptionKeys.js")
    28     if (keysjson == nil) or (keysjson[level] == nil) then
    51     if (keysjson == nil) or (keysjson[level] == nil) then
    29         return nil
    52         return nil
    30     end
    53     end
    31 
    54 
    32     local identifier = keysjson[level]
    55     local identifier = keysjson[level]
    53     end
    76     end
    54 
    77 
    55     return nil
    78     return nil
    56 end
    79 end
    57 
    80 
    58 local function showHint(basedir)
    81 local function getHint(basedir)
    59     local f = io.open(basedir .. "/.password.hint", "r")
    82     local f = io.open(basedir .. "/.password.hint", "r")
    60     if (f == nil) then
    83     if (f == nil) then
    61         return
    84         return
    62     end
    85     end
    63 
    86 
    64     local str = f:read("*all")
    87     local str = "(hint is '" .. f:read("*all") .. "')."
    65     f:close()
    88     f:close()
    66 
    89     --print(str)
    67     print("(hint is '" .. str .. "').")
    90     return str
    68 end
    91 end
    69 
    92 
    70 
    93 
    71 function loadContents(basedir)
    94 function loadContents(basedir)
    72     return load_json(basedir .. "/contents.js");
    95     return load_json(basedir .. "/contents.js")
    73 end
    96 end
    74 
    97 
    75 local function shouldFilterOut(filter, type, name, url)
    98 local function build_secret_menuitem(menu, type, str, hidden)
    76     if filter == nil then
    99     if str == nil then
    77         return false   -- no filter? Don't filter.
   100         return nil
    78     elseif type == "system.Tombstone" then
   101     end
    79         return true    -- I guess those are dead items?
   102 
    80     elseif string.find(string.lower(name), filter) ~= nil then
   103     local valuestr = str
    81         return false   -- matched keep-filter on name
   104     if hidden == true then
    82     elseif string.find(string.lower(url), filter) ~= nil then
   105         valuestr = "*****"
    83         return false   -- matched keep-filter on URL
   106     end
    84     end
   107     local text = type .. " " .. valuestr
    85     return true  -- didn't match our keep-filter. Chuck it.
   108 
       
   109     local callback = function()
       
   110         copyToClipboard(str)
       
   111         --print("Copied data [" .. str .. "] to clipboard.")
       
   112     end
       
   113     return appendGuiMenuItem(menu, text, callback)
       
   114 end
       
   115 
       
   116 
       
   117 local secret_menuitem_builders = {}
       
   118 
       
   119 local function build_secret_menuitem_webform(menu, info, secure)
       
   120     local addthis = false
       
   121     local username = nil
       
   122     local password = nil
       
   123     local email = nil
       
   124     for i,v in ipairs(secure.fields) do
       
   125         --print(info.name .. ": " .. v.type .. ", " .. v.value)
       
   126         local ignored = false
       
   127         if (v.type == "P") and (password == nil) and (v.value ~= "") then
       
   128             password = v.value
       
   129         elseif (v.type == "T") and (usenname == nil) and (v.value ~= "") then
       
   130             username = v.value
       
   131         elseif (v.type == "E") and (email == nil) and (v.value ~= "") then
       
   132             email = v.value
       
   133         else
       
   134             ignored = true
       
   135         end
       
   136 
       
   137         if not ignored then
       
   138             addthis = true
       
   139         end
       
   140     end
       
   141 
       
   142     if addthis then
       
   143         if (username ~= nil) and (email ~= nil) and (email == username) then
       
   144             email = nil
       
   145         end
       
   146 
       
   147         build_secret_menuitem(menu, "username", username)
       
   148         build_secret_menuitem(menu, "email", email)
       
   149         build_secret_menuitem(menu, "password", password, true)
       
   150     end
       
   151 end
       
   152 secret_menuitem_builders["webforms.WebForm"] = build_secret_menuitem_webform
       
   153 
       
   154 
       
   155 local function build_secret_menuitem_password(menu, info, secure)
       
   156     build_secret_menuitem(menu, "password", secure.password, true)
       
   157 end
       
   158 secret_menuitem_builders["passwords.Password"] = build_secret_menuitem_password
       
   159 
       
   160 
       
   161 local function build_secret_menuitem_bankacctus(menu, info, secure)
       
   162     -- !!! FIXME: there's more data than this in a generic dictionary.
       
   163     build_secret_menuitem(menu, "Account type", secure.accountType)
       
   164     build_secret_menuitem(menu, "Routing number", secure.routingNo)
       
   165     build_secret_menuitem(menu, "Account number", secure.accountNo)
       
   166     build_secret_menuitem(menu, "Bank name", secure.bankName)
       
   167     build_secret_menuitem(menu, "Owner", secure.owner)
       
   168 end
       
   169 secret_menuitem_builders["wallet.financial.BankAccountUS"] = build_secret_menuitem_bankacctus
       
   170 
       
   171 
       
   172 local function build_secret_menuitem_driverslic(menu, info, secure)
       
   173     -- !!! FIXME: there's more data than this in a generic dictionary.
       
   174     local birthdate = secure.birthdate_yy .. "/" .. string.sub("00" .. secure.birthdate_mm, -2) .. "/" .. string.sub("00" .. secure.birthdate_dd, -2)
       
   175     local expiredate = secure.expiry_date_yy .. "/" .. string.sub("00" .. secure.expiry_date_mm, -2)
       
   176     build_secret_menuitem(menu, "License number", secure.number)
       
   177     build_secret_menuitem(menu, "Class", secure.class)
       
   178     build_secret_menuitem(menu, "Expires", expiredate)
       
   179     build_secret_menuitem(menu, "State", secure.state)
       
   180     build_secret_menuitem(menu, "Country", secure.country)
       
   181     build_secret_menuitem(menu, "Conditions", secure.conditions)
       
   182     build_secret_menuitem(menu, "Full name", secure.fullname)
       
   183     build_secret_menuitem(menu, "Address", secure.address)
       
   184     build_secret_menuitem(menu, "Gender", secure.sex)
       
   185     build_secret_menuitem(menu, "Birthdate", birthdate)
       
   186     build_secret_menuitem(menu, "Height", secure.height)
       
   187 end
       
   188 secret_menuitem_builders["wallet.government.DriversLicense"] = build_secret_menuitem_driverslic
       
   189 
       
   190 
       
   191 local function build_secret_menuitem_membership(menu, info, secure)
       
   192     -- !!! FIXME: there's more data than this in a generic dictionary.
       
   193     build_secret_menuitem(menu, "Membership number", secure.membership_no)
       
   194 end
       
   195 secret_menuitem_builders["wallet.membership.Membership"] = build_secret_menuitem_membership
       
   196 
       
   197 
       
   198 local function build_secret_menuitem_creditcard(menu, info, secure)
       
   199     -- !!! FIXME: there's more data than this in a generic dictionary.
       
   200     local expiredate = secure.expiry_yy .. "/" .. string.sub("00" .. secure.expiry_mm, -2)
       
   201     build_secret_menuitem(menu, "Type", secure.type)
       
   202     build_secret_menuitem(menu, "CC number", secure.ccnum, true)
       
   203     build_secret_menuitem(menu, "CVV", secure.cvv, true)
       
   204     build_secret_menuitem(menu, "Expires", secure.expirydate)
       
   205     build_secret_menuitem(menu, "Card holder", secure.cardholder)
       
   206     build_secret_menuitem(menu, "Bank", secure.bank)
       
   207 end
       
   208 secret_menuitem_builders["wallet.financial.CreditCard"] = build_secret_menuitem_creditcard
       
   209 
       
   210 
       
   211 local function build_secret_menuitems(basedir, info, menu, password)
       
   212     local metadata = load_json(basedir .. "/" .. info.uuid .. ".1password")
       
   213     if metadata == nil then
       
   214         return
       
   215     end
       
   216 
       
   217     local plaintext = decryptBase64UsingKey(metadata.encrypted, loadKey(basedir, metadata.securityLevel, password))
       
   218     if plaintext == nil then
       
   219         return
       
   220     end
       
   221 
       
   222     local secure = load_json_str(plaintext, info.uuid)
       
   223     if secure == nil then
       
   224         return
       
   225     end
       
   226     --dumptable("secure " .. info.name, secure)
       
   227 
       
   228     local menuitem = appendGuiMenuItem(menu, info.name)
       
   229 
       
   230     if secret_menuitem_builders[info.type] == nil then
       
   231         print("WARNING: don't know how to handle items of type " .. info.type)
       
   232         dumptable("secure " .. info.type .. " (" .. info.name .. ")", secure)
       
   233         return
       
   234     end
       
   235 
       
   236     local submenu = makeGuiMenu()
       
   237     secret_menuitem_builders[info.type](submenu, info, secure)
       
   238     setGuiMenuItemSubmenu(menuitem, submenu)
    86 end
   239 end
    87 
   240 
    88 
   241 
    89 -- Mainline!
   242 -- Mainline!
    90 
   243 
    92 --    print("argv[" .. i .. "] = " .. v)
   245 --    print("argv[" .. i .. "] = " .. v)
    93 --end
   246 --end
    94 
   247 
    95 local basedir = "1Password/1Password.agilekeychain/data/default"  -- !!! FIXME
   248 local basedir = "1Password/1Password.agilekeychain/data/default"  -- !!! FIXME
    96 
   249 
    97 local passwordTypeNameMap = {
   250 local password = argv[2]
    98     ["wallet.financial.BankAccountUS"] = "Bank accounts",
   251 while password == nil do
    99     ["wallet.financial.CreditCard"] = "Credit cards",
   252     password = runGuiPasswordPrompt(getHint(basedir))
   100     ["webforms.WebForm"] = "Logins",
   253     if password == nil then
   101     ["system.Tombstone"] = "Dead items",
   254         os.exit(1)
   102     ["wallet.membership.Membership"] = "Memberships",
   255     end
   103     ["wallet.government.DriversLicense"] = "Drivers licenses",
   256     if loadKey(basedir, "SL5", password) == nil then
   104     ["passwords.Password"] = "Passwords",
   257         password = nil  -- wrong password
   105     -- !!! FIXME: more!
   258         local start = os.time()  -- cook the CPU for three seconds.
   106 }
   259         local now = start
       
   260         while os.difftime(now, start) < 3 do
       
   261             now = os.time()
       
   262         end
       
   263     end
       
   264 end
   107 
   265 
   108 local contents = loadContents(basedir)
   266 local contents = loadContents(basedir)
   109 local items = {}
   267 local items = {}
   110 for i,v in ipairs(contents) do
   268 for i,v in ipairs(contents) do
   111     local t = v[2]
   269     local t = v[2]
   112     if t ~= "system.Tombstone" then
   270     if items[t] == nil then
   113         if items[t] == nil then
   271         items[t] = {}
   114             items[t] = {}
   272     end
   115         end
   273     local bucket = items[t]
   116         local bucket = items[t]
   274     bucket[#bucket+1] = { uuid=v[1], type=t, name=v[3], url=v[4] }  -- !!! FIXME: there are more fields, don't know what they mean yet.
   117         bucket[#bucket+1] = { uuid=v[1], type=t, name=v[3], url=v[4] }  -- !!! FIXME: there are more fields, don't know what they mean yet.
       
   118     end
       
   119 end
   275 end
   120 contents = nil
   276 contents = nil
   121 
   277 
   122 local topmenu = makeGuiMenu()
   278 local topmenu = makeGuiMenu()
   123 for type,bucket in pairs(items) do
   279 for orderi,type in ipairs(passwordTypeOrdering) do
   124 print(type)
   280     local bucket = items[type]
   125     local realname = passwordTypeNameMap[type]
   281     local realname = passwordTypeNameMap[type]
   126     if realname == nil then
   282     if realname == nil then
   127         realname = type
   283         realname = type
   128     end
   284     end
   129     local menuitem = appendGuiMenuItem(topmenu, realname)
   285     local menuitem = appendGuiMenuItem(topmenu, realname)
   130     local submenu = makeGuiMenu()
   286     local submenu = makeGuiMenu()
       
   287     table.sort(bucket, function(a, b) return a.name < b.name end)
   131     for i,v in pairs(bucket) do
   288     for i,v in pairs(bucket) do
   132         local submenuitem = appendGuiMenuItem(submenu, v.name, function() print("Clicked on " .. v.name .. ", uuid is '" .. v.uuid .. "'") end)
   289         build_secret_menuitems(basedir, v, submenu, password)
   133     end
   290     end
   134     setGuiMenuItemSubmenu(menuitem, submenu)
   291     setGuiMenuItemSubmenu(menuitem, submenu)
   135 end
   292 end
   136 
   293 
   137 popupGuiMenu(topmenu)
   294 popupGuiMenu(topmenu)
   138 giveControlToGui()
   295 giveControlToGui()
   139 
   296 
   140 os.exit(1)
       
   141 
       
   142 
       
   143 local password = argv[3]
       
   144 if password == nil then
       
   145     showHint(basedir)
       
   146     io.write("password: ")
       
   147     password = io.read("*l")
       
   148 end
       
   149 
       
   150 if loadKey(basedir, "SL5", password) == nil then
       
   151     print("wrong password?\n")
       
   152     os.exit(1)
       
   153 end
       
   154 
       
   155 local filter = argv[2]
       
   156 if filter ~= nil then
       
   157     filter = string.lower(filter)
       
   158 end
       
   159 
       
   160 items = loadContents(basedir)
       
   161 for i,v in ipairs(items) do
       
   162     local type = v[2]
       
   163     local name = v[3]
       
   164     local url = v[4]
       
   165     if not shouldFilterOut(filter, type, name, url) then
       
   166         local metadata = load_json(basedir .. "/" .. v[1] .. ".1password")
       
   167         if metadata ~= nil then
       
   168             local plaintext = decryptBase64UsingKey(metadata.encrypted, loadKey(basedir, metadata.securityLevel, password))
       
   169             local username = nil
       
   170             local password = nil
       
   171             if plaintext ~= nil then
       
   172                 local secure = load_json_str(plaintext, v[1])
       
   173                 if type == "webforms.WebForm" then
       
   174                     for ii,vv in ipairs(secure.fields) do
       
   175                         if vv.type == "P" then
       
   176                             password = vv.value
       
   177                         elseif vv.type == "E" then
       
   178                             username = vv.value
       
   179                         end
       
   180                     end
       
   181                 elseif type == "passwords.Password" then
       
   182                     password = secure.password
       
   183                 end
       
   184             end
       
   185 
       
   186             print("item: " .. metadata.title)
       
   187             if username ~= nil then print("username: " .. username) end
       
   188             if password ~= nil then print("password: " .. password) end
       
   189 
       
   190         end
       
   191     end
       
   192 end
       
   193 
       
   194 -- end of 1pass.lua ...
   297 -- end of 1pass.lua ...
   195 
   298