onepass 0.0.2 → 0.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 780ae33029de7cf60dfa6a1b16feb41dbff4c4e7
4
- data.tar.gz: 087efe99c57afbc5e782b01b7dfd1774c30b731f
3
+ metadata.gz: f081e63d2a0fc4f35b2ca7698facbc79a27dbe68
4
+ data.tar.gz: 26e4b49e1e270d90ae4c00262d4b0d0bd68cf3fa
5
5
  SHA512:
6
- metadata.gz: 01fa89e1b5ee6a87c8ebd1cab37f88c4f98fc0fb638ad52f7b0d8e96d3799778c67c90a7dcf47ad4b41cecbbac53c481166b6ab06af865668932ce66f84df954
7
- data.tar.gz: 4ae603513c043f6545973729efcc40d3e1863e2566d725b05d1aa9ab5356f8538e5fdd6c8c6a38a768eb5d1b7c5f5b9fb27553e20fabfa6d435f7ab57b4884cd
6
+ metadata.gz: fa0258a8c9ab35aeba1062fbe697f7f46172d2d355158080d80597c7246d21225d32a32bf42a7315bfb29df1ccf8fe52a4dc03e17c7d488c04119d43602b12a1
7
+ data.tar.gz: 1f16989a6776547b046f6c80eb3075186c0c9c76297061c23b14debb9215cb8211298563f741db47fc6cbff7316690cbd7c5b0e4f326ff308b7bfb3f2f13ff99
data/OnePass.gemspec CHANGED
@@ -19,6 +19,7 @@ Gem::Specification.new do |spec|
19
19
  spec.require_paths = ["lib"]
20
20
 
21
21
  spec.add_dependency "sqlite3", "~> 1.3.9"
22
+ spec.add_dependency "CFPropertyList"
22
23
 
23
24
  spec.add_development_dependency "bundler", "~> 1.7"
24
25
  spec.add_development_dependency "rake", "~> 10.0"
@@ -1,3 +1,3 @@
1
1
  module OnePass
2
- VERSION = "0.0.2"
2
+ VERSION = "0.0.3"
3
3
  end
data/lib/OnePass.rb CHANGED
@@ -3,6 +3,7 @@ require "openssl"
3
3
  require "sqlite3"
4
4
  require "json"
5
5
  require "tempfile"
6
+ require "cfpropertylist"
6
7
 
7
8
  module OnePass
8
9
  class VerifyException < Exception
@@ -18,7 +19,7 @@ module OnePass
18
19
  if buf[0..7] != "opdata01"
19
20
  raise OnePass::Opdata::InvalidException.new("Header was incorrect")
20
21
  end
21
- @length = buf[8..15].unpack("V")[0]
22
+ @length = buf[8..15].unpack("Q<")[0]
22
23
  @mac = buf[-32..-1]
23
24
  if OpenSSL::HMAC.digest(OpenSSL::Digest::SHA256.new, mac, buf[0..-33]) != @mac
24
25
  raise OnePass::VerifyException.new("MAC doesn't match; verify failed. Check your encryption/mac keys.")
@@ -60,33 +61,48 @@ module OnePass
60
61
  db = SQLite3::Database.new(sqlite_file)
61
62
  db.execute "VACUUM;"
62
63
 
63
- # read profile data
64
+ @master_keys = []
65
+ @overview_keys = []
64
66
  @overviews = []
65
- @masters = []
66
- db.execute "SELECT id,master_key_data,overview_key_data,salt,iterations FROM profiles" do |profile|
67
-
68
- # derive the key from the password
69
- derived_key = OpenSSL::PKCS5.pbkdf2_hmac(master_password, profile[3], profile[4], 64, OpenSSL::Digest::SHA512.new)
70
- derived_encryption_key = derived_key[0..31]
71
- derived_mac_key = derived_key[32..-1]
72
-
73
- # try to unlock profile data. return fail if failed login
74
- overview_key_data = OnePass::Opdata.new(profile[2], derived_encryption_key, derived_mac_key)
75
- overview_key = OpenSSL::Digest::SHA512.new.digest(overview_key_data.data)
76
- overview_encryption_key, overview_mac_key = overview_key[0..31], overview_key[32..-1]
77
-
78
- # load overview opdata into object based format. overviews are stored decrypted for use later.
79
- # the encrypted data for the keys is included, but is not decrypted unless requested later
80
- db.execute "SELECT items.key_data, items.overview_data, item_details.data FROM items INNER JOIN item_details ON items.id=item_details.item_id WHERE items.profile_id=#{profile[0]};" do |row|
81
- overview = OnePass::Opdata.new(row[1], overview_encryption_key, overview_mac_key)
82
- json = JSON.parse(overview.data).merge({profile: profile[0], key_data: row[0], data: row[2]})
83
- @overviews << json
84
- end
85
-
86
- # decrypt the master key for use later
87
- master_key_data = OnePass::Opdata.new(profile[1], derived_encryption_key, derived_mac_key)
88
- master_key = OpenSSL::Digest::SHA512.new.digest(master_key_data.data)
89
- @masters[profile[0]] = {enc_key: master_key[0..31], mac_key: master_key[32..-1]}
67
+ # read master profile
68
+ master_profile = db.execute "SELECT id,master_key_data,overview_key_data,salt,iterations FROM profiles WHERE attributes_data IS NULL"
69
+ raise "Found more than one master profile!" unless master_profile.length == 1
70
+ master_profile.flatten!
71
+ master_profile_id = master_profile[0]
72
+
73
+ # derive the key from the password
74
+ derived_key = OpenSSL::PKCS5.pbkdf2_hmac(master_password, master_profile[3], master_profile[4], 64, OpenSSL::Digest::SHA512.new)
75
+ derived_encryption_key = derived_key[0..31]
76
+ derived_mac_key = derived_key[32..-1]
77
+
78
+ # Obtain the master profile master keys
79
+ master_key_data = OnePass::Opdata.new(master_profile[1], derived_encryption_key, derived_mac_key)
80
+ master_key = OpenSSL::Digest::SHA512.new.digest(master_key_data.data)
81
+ @master_keys[master_profile_id] = {enc_key: master_key[0..31], mac_key: master_key[32..-1]}
82
+
83
+ # Obtain the master profile overview keys
84
+ overview_key_data = OnePass::Opdata.new(master_profile[2], derived_encryption_key, derived_mac_key)
85
+ overview_key = OpenSSL::Digest::SHA512.new.digest(overview_key_data.data)
86
+ @overview_keys[master_profile_id] = { enc_key: overview_key[0..31], mac_key: overview_key[32..-1] }
87
+
88
+ # Obtain keys for remaining profiles
89
+ db.execute "SELECT id,attributes_data FROM profiles WHERE attributes_data IS NOT NULL" do |profile|
90
+ attributes_data = OnePass::Opdata.new(profile[1], @overview_keys[master_profile_id][:enc_key], @overview_keys[master_profile_id][:mac_key])
91
+ plist = CFPropertyList.native_types(CFPropertyList::List.new(:data => attributes_data.data).value)
92
+ overview_key_data = plist['$objects'][plist['$top']['overviewKey']]
93
+ overview_key = OpenSSL::Digest::SHA512.new.digest(overview_key_data)
94
+ @overview_keys[profile[0]] = { enc_key: overview_key[0..31], mac_key: overview_key[32..-1] }
95
+ master_key_data = plist['$objects'][plist['$top']['masterKey']]
96
+ master_key = OpenSSL::Digest::SHA512.new.digest(master_key_data)
97
+ @master_keys[profile[0]] = { enc_key: master_key[0..31], mac_key: master_key[32..-1] }
98
+ end
99
+
100
+ # load overview opdata into object based format. overviews are stored decrypted for use later.
101
+ # the encrypted data for the keys is included, but is not decrypted unless requested later
102
+ 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|
103
+ overview = OnePass::Opdata.new(item[2], @overview_keys[item[0]][:enc_key], @overview_keys[item[0]][:mac_key])
104
+ json = JSON.parse(overview.data).merge({profile: item[0], key_data: item[1], data: item[3]})
105
+ @overviews << json
90
106
  end
91
107
 
92
108
  db.close
@@ -104,20 +120,26 @@ module OnePass
104
120
  return nil
105
121
  end
106
122
 
107
- def decrypt(overview)
123
+ def decrypt(overview, all = nil)
108
124
  key_data = overview[:key_data][0..-33]
109
125
  mac = overview[:key_data][-32..-1]
110
126
  profile = overview[:profile]
111
- if OpenSSL::HMAC.digest(OpenSSL::Digest::SHA256.new, @masters[profile][:mac_key], key_data) != mac
127
+ if OpenSSL::HMAC.digest(OpenSSL::Digest::SHA256.new, @master_keys[profile][:mac_key], key_data) != mac
112
128
  raise VerifyException.new("The item's encryption key couldn't be verified.")
113
129
  end
114
130
  cipher = OpenSSL::Cipher::AES.new(256, :CBC)
115
131
  cipher.decrypt
116
132
  cipher.padding = 0
117
133
  cipher.iv = key_data[0..15]
118
- cipher.key = @masters[profile][:enc_key]
134
+ cipher.key = @master_keys[profile][:enc_key]
119
135
  key_data = cipher.update(key_data[16..-1]) + cipher.final
120
- return JSON.parse(OnePass::Opdata.new(overview[:data],key_data[0..31],key_data[32..-1]).data)["password"]
136
+ results = JSON.parse(OnePass::Opdata.new(overview[:data],key_data[0..31],key_data[32..-1]).data)
137
+ password = if results.has_key?("password")
138
+ results["password"]
139
+ elsif results.has_key?("fields")
140
+ results["fields"].find { |h| h["designation"] == "password" }["value"]
141
+ end
142
+ return all ? results : password
121
143
  end
122
144
  end
123
145
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: onepass
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 0.0.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kai Lieth
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-11-10 00:00:00.000000000 Z
11
+ date: 2015-04-24 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: sqlite3
@@ -24,6 +24,20 @@ dependencies:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
26
  version: 1.3.9
27
+ - !ruby/object:Gem::Dependency
28
+ name: CFPropertyList
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
27
41
  - !ruby/object:Gem::Dependency
28
42
  name: bundler
29
43
  requirement: !ruby/object:Gem::Requirement
@@ -104,7 +118,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
104
118
  version: '0'
105
119
  requirements: []
106
120
  rubyforge_project:
107
- rubygems_version: 2.4.2
121
+ rubygems_version: 2.4.6
108
122
  signing_key:
109
123
  specification_version: 4
110
124
  summary: Decrypt the secrets stored in 1Password 4