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