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
|
# File 'lib/OnePass.rb', line 44
def initialize(master_password, path = nil)
path ||= "#{ENV["HOME"]}/Library/Application Support/1Password 4/Data/OnePassword.sqlite"
raise "Can't find sqlite db at #{path}" unless File.exist? path
db_filename = File.basename(path)
dir_path = File.dirname(path)
Dir.mktmpdir('OnePass') do |tmpdir|
FileUtils.cp_r("#{dir_path}/.", tmpdir)
sqlite_file = File.join(tmpdir, db_filename)
db = SQLite3::Database.new(sqlite_file)
db.execute "VACUUM;"
@master_keys = []
@overview_keys = []
@overviews = []
master_profile = db.execute "SELECT id,master_key_data,overview_key_data,salt,iterations FROM profiles WHERE attributes_data IS NULL"
raise "Found more than one master profile!" unless master_profile.length == 1
master_profile.flatten!
master_profile_id = master_profile[0]
derived_key = OpenSSL::PKCS5.pbkdf2_hmac(master_password, master_profile[3], master_profile[4], 64, OpenSSL::Digest::SHA512.new)
derived_encryption_key = derived_key[0..31]
derived_mac_key = derived_key[32..-1]
master_key_data = OnePass::Opdata.new(master_profile[1], derived_encryption_key, derived_mac_key)
master_key = OpenSSL::Digest::SHA512.new.digest(master_key_data.data)
@master_keys[master_profile_id] = {enc_key: master_key[0..31], mac_key: master_key[32..-1]}
overview_key_data = OnePass::Opdata.new(master_profile[2], derived_encryption_key, derived_mac_key)
overview_key = OpenSSL::Digest::SHA512.new.digest(overview_key_data.data)
@overview_keys[master_profile_id] = { enc_key: overview_key[0..31], mac_key: overview_key[32..-1] }
db.execute "SELECT id,attributes_data FROM profiles WHERE attributes_data IS NOT NULL" do |profile|
attributes_data = OnePass::Opdata.new(profile[1], @overview_keys[master_profile_id][:enc_key], @overview_keys[master_profile_id][:mac_key])
plist = CFPropertyList.native_types(CFPropertyList::List.new(:data => attributes_data.data).value)
overview_key_data = plist['$objects'][plist['$top']['overviewKey']]
overview_key = OpenSSL::Digest::SHA512.new.digest(overview_key_data)
@overview_keys[profile[0]] = { enc_key: overview_key[0..31], mac_key: overview_key[32..-1] }
master_key_data = plist['$objects'][plist['$top']['masterKey']]
master_key = OpenSSL::Digest::SHA512.new.digest(master_key_data)
@master_keys[profile[0]] = { enc_key: master_key[0..31], mac_key: master_key[32..-1] }
end
db.execute "SELECT items.profile_id, items.key_data, items.overview_data, item_details.data FROM items INNER JOIN item_details ON items.id=item_details.item_id" do |item|
overview = OnePass::Opdata.new(item[2], @overview_keys[item[0]][:enc_key], @overview_keys[item[0]][:mac_key])
json = JSON.parse(overview.data).merge({profile: item[0], key_data: item[1], data: item[3]})
@overviews << json
end
db.close
end
end
|