one_password 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +17 -0
- data/.rspec +2 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +29 -0
- data/Rakefile +21 -0
- data/lib/one_password.rb +19 -0
- data/lib/one_password/encryption.rb +74 -0
- data/lib/one_password/encryption_key.rb +34 -0
- data/lib/one_password/errors.rb +13 -0
- data/lib/one_password/item.rb +156 -0
- data/lib/one_password/keychain.rb +47 -0
- data/lib/one_password/profile.rb +94 -0
- data/lib/one_password/version.rb +3 -0
- data/one_password.gemspec +27 -0
- data/spec/fixtures/1Password.agilekeychain/1Password.html +3870 -0
- data/spec/fixtures/1Password.agilekeychain/a/default/thumb/l/0EDE2B13D7AC4E2C9105842682ACB187 +0 -0
- data/spec/fixtures/1Password.agilekeychain/a/default/thumb/l/0EDE2B13D7AC4E2C9105842682ACB187.def +1 -0
- data/spec/fixtures/1Password.agilekeychain/a/default/thumb/l/13C8E12AC8E54B1F873BAB0824E521BC +0 -0
- data/spec/fixtures/1Password.agilekeychain/a/default/thumb/l/13C8E12AC8E54B1F873BAB0824E521BC.def +1 -0
- data/spec/fixtures/1Password.agilekeychain/a/default/thumb/l/2A632FDD32F5445E91EB5636C7580447 +0 -0
- data/spec/fixtures/1Password.agilekeychain/a/default/thumb/l/2A632FDD32F5445E91EB5636C7580447.def +1 -0
- data/spec/fixtures/1Password.agilekeychain/a/default/thumb/l/358B7411EB8B45CD9CE592ED16F3E9DE +0 -0
- data/spec/fixtures/1Password.agilekeychain/a/default/thumb/l/358B7411EB8B45CD9CE592ED16F3E9DE.def +1 -0
- data/spec/fixtures/1Password.agilekeychain/a/default/thumb/l/468B1E24F93B413DAD57ABE6F1C01DF6 +0 -0
- data/spec/fixtures/1Password.agilekeychain/a/default/thumb/l/468B1E24F93B413DAD57ABE6F1C01DF6.def +1 -0
- data/spec/fixtures/1Password.agilekeychain/a/default/thumb/l/5ADFF73C09004C448D45565BC4750DE2 +0 -0
- data/spec/fixtures/1Password.agilekeychain/a/default/thumb/l/5ADFF73C09004C448D45565BC4750DE2.def +1 -0
- data/spec/fixtures/1Password.agilekeychain/a/default/thumb/l/72366D161D9E43D98E58EB801DAD1EF8 +0 -0
- data/spec/fixtures/1Password.agilekeychain/a/default/thumb/l/72366D161D9E43D98E58EB801DAD1EF8.def +1 -0
- data/spec/fixtures/1Password.agilekeychain/a/default/thumb/l/D8F79F17D6384808848B213EB4946ECA +0 -0
- data/spec/fixtures/1Password.agilekeychain/a/default/thumb/l/D8F79F17D6384808848B213EB4946ECA.def +1 -0
- data/spec/fixtures/1Password.agilekeychain/a/default/thumb/l/EC0A40400ABB4B16926B7417E95C9669 +0 -0
- data/spec/fixtures/1Password.agilekeychain/a/default/thumb/l/EC0A40400ABB4B16926B7417E95C9669.def +1 -0
- data/spec/fixtures/1Password.agilekeychain/a/default/thumb/l/F3707FA58EA7480884BC6A662658E039 +0 -0
- data/spec/fixtures/1Password.agilekeychain/a/default/thumb/l/F3707FA58EA7480884BC6A662658E039.def +1 -0
- data/spec/fixtures/1Password.agilekeychain/a/default/thumb/l/F5F099B210F248348E22934DDC3338B2 +0 -0
- data/spec/fixtures/1Password.agilekeychain/a/default/thumb/l/F5F099B210F248348E22934DDC3338B2.def +1 -0
- data/spec/fixtures/1Password.agilekeychain/a/default/thumb/l/F78CEC04078743B6975511A6FDDBED7E +0 -0
- data/spec/fixtures/1Password.agilekeychain/a/default/thumb/l/F78CEC04078743B6975511A6FDDBED7E.def +1 -0
- data/spec/fixtures/1Password.agilekeychain/a/default/thumb/m/0EDE2B13D7AC4E2C9105842682ACB187 +0 -0
- data/spec/fixtures/1Password.agilekeychain/a/default/thumb/m/0EDE2B13D7AC4E2C9105842682ACB187.def +1 -0
- data/spec/fixtures/1Password.agilekeychain/a/default/thumb/m/13C8E12AC8E54B1F873BAB0824E521BC +0 -0
- data/spec/fixtures/1Password.agilekeychain/a/default/thumb/m/13C8E12AC8E54B1F873BAB0824E521BC.def +1 -0
- data/spec/fixtures/1Password.agilekeychain/a/default/thumb/m/2A632FDD32F5445E91EB5636C7580447 +0 -0
- data/spec/fixtures/1Password.agilekeychain/a/default/thumb/m/2A632FDD32F5445E91EB5636C7580447.def +1 -0
- data/spec/fixtures/1Password.agilekeychain/a/default/thumb/m/358B7411EB8B45CD9CE592ED16F3E9DE +0 -0
- data/spec/fixtures/1Password.agilekeychain/a/default/thumb/m/358B7411EB8B45CD9CE592ED16F3E9DE.def +1 -0
- data/spec/fixtures/1Password.agilekeychain/a/default/thumb/m/468B1E24F93B413DAD57ABE6F1C01DF6 +0 -0
- data/spec/fixtures/1Password.agilekeychain/a/default/thumb/m/468B1E24F93B413DAD57ABE6F1C01DF6.def +1 -0
- data/spec/fixtures/1Password.agilekeychain/a/default/thumb/m/5ADFF73C09004C448D45565BC4750DE2 +0 -0
- data/spec/fixtures/1Password.agilekeychain/a/default/thumb/m/5ADFF73C09004C448D45565BC4750DE2.def +1 -0
- data/spec/fixtures/1Password.agilekeychain/a/default/thumb/m/72366D161D9E43D98E58EB801DAD1EF8 +0 -0
- data/spec/fixtures/1Password.agilekeychain/a/default/thumb/m/72366D161D9E43D98E58EB801DAD1EF8.def +1 -0
- data/spec/fixtures/1Password.agilekeychain/a/default/thumb/m/D8F79F17D6384808848B213EB4946ECA +0 -0
- data/spec/fixtures/1Password.agilekeychain/a/default/thumb/m/D8F79F17D6384808848B213EB4946ECA.def +1 -0
- data/spec/fixtures/1Password.agilekeychain/a/default/thumb/m/EC0A40400ABB4B16926B7417E95C9669 +0 -0
- data/spec/fixtures/1Password.agilekeychain/a/default/thumb/m/EC0A40400ABB4B16926B7417E95C9669.def +1 -0
- data/spec/fixtures/1Password.agilekeychain/a/default/thumb/m/F3707FA58EA7480884BC6A662658E039 +0 -0
- data/spec/fixtures/1Password.agilekeychain/a/default/thumb/m/F3707FA58EA7480884BC6A662658E039.def +1 -0
- data/spec/fixtures/1Password.agilekeychain/a/default/thumb/m/F5F099B210F248348E22934DDC3338B2 +0 -0
- data/spec/fixtures/1Password.agilekeychain/a/default/thumb/m/F5F099B210F248348E22934DDC3338B2.def +1 -0
- data/spec/fixtures/1Password.agilekeychain/a/default/thumb/m/F78CEC04078743B6975511A6FDDBED7E +0 -0
- data/spec/fixtures/1Password.agilekeychain/a/default/thumb/m/F78CEC04078743B6975511A6FDDBED7E.def +1 -0
- data/spec/fixtures/1Password.agilekeychain/a/default/thumb/s/0EDE2B13D7AC4E2C9105842682ACB187 +0 -0
- data/spec/fixtures/1Password.agilekeychain/a/default/thumb/s/0EDE2B13D7AC4E2C9105842682ACB187.def +1 -0
- data/spec/fixtures/1Password.agilekeychain/a/default/thumb/s/13C8E12AC8E54B1F873BAB0824E521BC +0 -0
- data/spec/fixtures/1Password.agilekeychain/a/default/thumb/s/13C8E12AC8E54B1F873BAB0824E521BC.def +1 -0
- data/spec/fixtures/1Password.agilekeychain/a/default/thumb/s/2A632FDD32F5445E91EB5636C7580447 +0 -0
- data/spec/fixtures/1Password.agilekeychain/a/default/thumb/s/2A632FDD32F5445E91EB5636C7580447.def +1 -0
- data/spec/fixtures/1Password.agilekeychain/a/default/thumb/s/358B7411EB8B45CD9CE592ED16F3E9DE +0 -0
- data/spec/fixtures/1Password.agilekeychain/a/default/thumb/s/358B7411EB8B45CD9CE592ED16F3E9DE.def +1 -0
- data/spec/fixtures/1Password.agilekeychain/a/default/thumb/s/468B1E24F93B413DAD57ABE6F1C01DF6 +0 -0
- data/spec/fixtures/1Password.agilekeychain/a/default/thumb/s/468B1E24F93B413DAD57ABE6F1C01DF6.def +1 -0
- data/spec/fixtures/1Password.agilekeychain/a/default/thumb/s/5ADFF73C09004C448D45565BC4750DE2 +0 -0
- data/spec/fixtures/1Password.agilekeychain/a/default/thumb/s/5ADFF73C09004C448D45565BC4750DE2.def +1 -0
- data/spec/fixtures/1Password.agilekeychain/a/default/thumb/s/72366D161D9E43D98E58EB801DAD1EF8 +0 -0
- data/spec/fixtures/1Password.agilekeychain/a/default/thumb/s/72366D161D9E43D98E58EB801DAD1EF8.def +1 -0
- data/spec/fixtures/1Password.agilekeychain/a/default/thumb/s/D8F79F17D6384808848B213EB4946ECA +0 -0
- data/spec/fixtures/1Password.agilekeychain/a/default/thumb/s/D8F79F17D6384808848B213EB4946ECA.def +1 -0
- data/spec/fixtures/1Password.agilekeychain/a/default/thumb/s/EC0A40400ABB4B16926B7417E95C9669 +0 -0
- data/spec/fixtures/1Password.agilekeychain/a/default/thumb/s/EC0A40400ABB4B16926B7417E95C9669.def +1 -0
- data/spec/fixtures/1Password.agilekeychain/a/default/thumb/s/F3707FA58EA7480884BC6A662658E039 +0 -0
- data/spec/fixtures/1Password.agilekeychain/a/default/thumb/s/F3707FA58EA7480884BC6A662658E039.def +1 -0
- data/spec/fixtures/1Password.agilekeychain/a/default/thumb/s/F5F099B210F248348E22934DDC3338B2 +0 -0
- data/spec/fixtures/1Password.agilekeychain/a/default/thumb/s/F5F099B210F248348E22934DDC3338B2.def +1 -0
- data/spec/fixtures/1Password.agilekeychain/a/default/thumb/s/F78CEC04078743B6975511A6FDDBED7E +0 -0
- data/spec/fixtures/1Password.agilekeychain/a/default/thumb/s/F78CEC04078743B6975511A6FDDBED7E.def +1 -0
- data/spec/fixtures/1Password.agilekeychain/config/buildnum +1 -0
- data/spec/fixtures/1Password.agilekeychain/config/use-thumbnails +1 -0
- data/spec/fixtures/1Password.agilekeychain/data/default/.1password.keys +0 -0
- data/spec/fixtures/1Password.agilekeychain/data/default/.password.hint +1 -0
- data/spec/fixtures/1Password.agilekeychain/data/default/0EDE2B13D7AC4E2C9105842682ACB187.1password +1 -0
- data/spec/fixtures/1Password.agilekeychain/data/default/13C8E12AC8E54B1F873BAB0824E521BC.1password +1 -0
- data/spec/fixtures/1Password.agilekeychain/data/default/1password.keys +0 -0
- data/spec/fixtures/1Password.agilekeychain/data/default/27DCFA2810B24083A3ECC7CEABC7C0A9.1password +1 -0
- data/spec/fixtures/1Password.agilekeychain/data/default/2A632FDD32F5445E91EB5636C7580447.1password +1 -0
- data/spec/fixtures/1Password.agilekeychain/data/default/358B7411EB8B45CD9CE592ED16F3E9DE.1password +1 -0
- data/spec/fixtures/1Password.agilekeychain/data/default/3A47A0E3FEE948ADA9028FF0DA053CDB.1password +1 -0
- data/spec/fixtures/1Password.agilekeychain/data/default/468B1E24F93B413DAD57ABE6F1C01DF6.1password +1 -0
- data/spec/fixtures/1Password.agilekeychain/data/default/4E36C011EE8348B1B24418218B04018C.1password +1 -0
- data/spec/fixtures/1Password.agilekeychain/data/default/5ADFF73C09004C448D45565BC4750DE2.1password +1 -0
- data/spec/fixtures/1Password.agilekeychain/data/default/72366D161D9E43D98E58EB801DAD1EF8.1password +1 -0
- data/spec/fixtures/1Password.agilekeychain/data/default/D06307ADA44C4031BA2FF4B174DE79CB.1password +1 -0
- data/spec/fixtures/1Password.agilekeychain/data/default/D1820AA8CB534AC6A4B5A2C0263FD3B2.1password +1 -0
- data/spec/fixtures/1Password.agilekeychain/data/default/D8F79F17D6384808848B213EB4946ECA.1password +1 -0
- data/spec/fixtures/1Password.agilekeychain/data/default/E482B70C038D4DD78A0940728FA737BF.1password +1 -0
- data/spec/fixtures/1Password.agilekeychain/data/default/EC0A40400ABB4B16926B7417E95C9669.1password +1 -0
- data/spec/fixtures/1Password.agilekeychain/data/default/F3707FA58EA7480884BC6A662658E039.1password +1 -0
- data/spec/fixtures/1Password.agilekeychain/data/default/F5F099B210F248348E22934DDC3338B2.1password +1 -0
- data/spec/fixtures/1Password.agilekeychain/data/default/F7883ADDE5944B349ABB5CBEC20F39BE.1password +1 -0
- data/spec/fixtures/1Password.agilekeychain/data/default/F78CEC04078743B6975511A6FDDBED7E.1password +1 -0
- data/spec/fixtures/1Password.agilekeychain/data/default/contents.js +1 -0
- data/spec/fixtures/1Password.agilekeychain/data/default/encryptionKeys.js +1 -0
- data/spec/fixtures/1Password.tsv +20 -0
- data/spec/fixtures/config.ru +1 -0
- data/spec/one_password/keychain_spec.rb +78 -0
- data/spec/spec_helper.rb +15 -0
- metadata +370 -0
data/.gitignore
ADDED
data/.rspec
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2013 Alexander Semyonov
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
# OnePassword
|
2
|
+
|
3
|
+
TODO: Write a gem description
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
gem 'one_password'
|
10
|
+
|
11
|
+
And then execute:
|
12
|
+
|
13
|
+
$ bundle
|
14
|
+
|
15
|
+
Or install it yourself as:
|
16
|
+
|
17
|
+
$ gem install one_password
|
18
|
+
|
19
|
+
## Usage
|
20
|
+
|
21
|
+
TODO: Write usage instructions here
|
22
|
+
|
23
|
+
## Contributing
|
24
|
+
|
25
|
+
1. Fork it
|
26
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
27
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
28
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
29
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'bundler/gem_tasks'
|
2
|
+
|
3
|
+
begin
|
4
|
+
require 'yard'
|
5
|
+
YARD::Rake::YardocTask.new(:doc)
|
6
|
+
rescue LoadError
|
7
|
+
task :doc do
|
8
|
+
abort 'YARD is not available. In order to run yardoc, you must run: `bundle install``'
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
begin
|
13
|
+
require 'rspec/core/rake_task'
|
14
|
+
RSpec::Core::RakeTask.new(:spec)
|
15
|
+
rescue LoadError
|
16
|
+
task :spec do
|
17
|
+
abort 'RSpec is not available. In order to run specs, you must run: `bundle install`'
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
task default: :spec
|
data/lib/one_password.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'one_password/version'
|
2
|
+
|
3
|
+
module OnePassword
|
4
|
+
WEBFORMS = 'webforms.WebForm'
|
5
|
+
FOLDERS = 'system.folder.Regular'
|
6
|
+
NOTES = 'securenotes.SecureNote'
|
7
|
+
IDENTITIES = 'identities.Identity'
|
8
|
+
PASSWORDS = 'passwords.Password'
|
9
|
+
WALLET = 'wallet'
|
10
|
+
SOFTWARE_LICENSES = 'wallet.computer.License'
|
11
|
+
TRASHED = 'trashed'
|
12
|
+
ACCOUNT = 'account'
|
13
|
+
ACCOUNT_ONLINESERVICE = 'wallet.onlineservices.'
|
14
|
+
ACCOUNT_COMPUTER = 'wallet.computer.'
|
15
|
+
CATEGORIES = [WEBFORMS, NOTES, WALLET, PASSWORDS, IDENTITIES, SOFTWARE_LICENSES, :folders,
|
16
|
+
ACCOUNT, TRASHED,].map { |type| type.to_sym }
|
17
|
+
end
|
18
|
+
|
19
|
+
require 'one_password/keychain'
|
@@ -0,0 +1,74 @@
|
|
1
|
+
require 'base64'
|
2
|
+
require 'openssl'
|
3
|
+
require 'pbkdf2'
|
4
|
+
require 'cgi'
|
5
|
+
|
6
|
+
module OnePassword
|
7
|
+
class Encryption
|
8
|
+
ZERO_IV = "\x00" * 8
|
9
|
+
NR = 10
|
10
|
+
NK = 4
|
11
|
+
|
12
|
+
def self.decrypt_using_pbkdf2(data, password, iterations)
|
13
|
+
encrypted = Base64.decode64(data)
|
14
|
+
salt = ZERO_IV
|
15
|
+
if salted?(encrypted)
|
16
|
+
salt = encrypted[8, 8]
|
17
|
+
encrypted = encrypted[16..-1]
|
18
|
+
end
|
19
|
+
|
20
|
+
derived_key = OpenSSL::PKCS5.pbkdf2_hmac_sha1(password, salt, iterations, 32)
|
21
|
+
|
22
|
+
key = derived_key.slice(0..15)
|
23
|
+
iv = derived_key.slice(16..-1)
|
24
|
+
|
25
|
+
decrypt_using_key_and_ivec(encrypted, key, iv)
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.decrypt_using_key(encrypted, encryption_key)
|
29
|
+
encrypted = Base64.decode64(encrypted)
|
30
|
+
|
31
|
+
if Encryption.salted?(encrypted)
|
32
|
+
salt = encrypted[8, 8]
|
33
|
+
encrypted = encrypted[16..-1]
|
34
|
+
|
35
|
+
key, iv = Encryption.open_ssl_key(encryption_key, salt)
|
36
|
+
else
|
37
|
+
key = OpenSSL::Digest::MD5.digest(encryption_key)
|
38
|
+
iv = Encryption::ZERO_IV
|
39
|
+
end
|
40
|
+
|
41
|
+
plain_text = Encryption.decrypt_using_key_and_ivec(encrypted, key, iv)
|
42
|
+
|
43
|
+
|
44
|
+
CGI::unescape(CGI::escape(plain_text))
|
45
|
+
|
46
|
+
end
|
47
|
+
|
48
|
+
def self.decrypt_using_key_and_ivec(encrypted, key, iv)
|
49
|
+
aes = OpenSSL::Cipher.new('AES-128-CBC')
|
50
|
+
aes.decrypt
|
51
|
+
aes.key = key
|
52
|
+
aes.iv = iv
|
53
|
+
aes.update(encrypted) << aes.final
|
54
|
+
end
|
55
|
+
|
56
|
+
def self.salted?(string)
|
57
|
+
(string =~ /\ASalted__/)
|
58
|
+
end
|
59
|
+
|
60
|
+
def self.open_ssl_key(password, salt)
|
61
|
+
rounds = NR >= 12 ? 3 : 2
|
62
|
+
data00 = password + salt
|
63
|
+
md5_hash = [OpenSSL::Digest::MD5.digest(data00)]
|
64
|
+
result = md5_hash[0]
|
65
|
+
1.upto(rounds - 1) do |i|
|
66
|
+
md5_hash[i] = OpenSSL::Digest::MD5.digest(md5_hash[i - 1] + data00)
|
67
|
+
result += md5_hash[i]
|
68
|
+
end
|
69
|
+
key = result.slice(0..(4 * NK - 1))
|
70
|
+
iv = result.slice((4 * NK)..(4 * NK + 15))
|
71
|
+
[key, iv]
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'one_password/encryption'
|
2
|
+
|
3
|
+
module OnePassword
|
4
|
+
class EncryptionKey
|
5
|
+
#noinspection RubyResolve
|
6
|
+
attr_accessor :profile, :data, :validation, :level, :iterations, :identifier
|
7
|
+
|
8
|
+
def initialize(profile, data)
|
9
|
+
@profile = profile
|
10
|
+
data.each do |name, value|
|
11
|
+
send("#{name}=", value)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
#noinspection RubyResolve
|
16
|
+
def iterations=(iterations)
|
17
|
+
@iterations = iterations.to_i
|
18
|
+
@iterations = 1000 if @iterations < 1000
|
19
|
+
@iterations
|
20
|
+
end
|
21
|
+
|
22
|
+
def decrypt(password=self.profile.password)
|
23
|
+
@decrypted_key = Encryption.decrypt_using_pbkdf2(data, password, iterations)
|
24
|
+
end
|
25
|
+
|
26
|
+
def decrypted_key
|
27
|
+
@decrypted_key || decrypt
|
28
|
+
end
|
29
|
+
|
30
|
+
def valid?
|
31
|
+
Encryption.decrypt_using_key(validation, decrypted_key) == decrypted_key
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,156 @@
|
|
1
|
+
require 'one_password/encryption'
|
2
|
+
require 'active_support/core_ext'
|
3
|
+
|
4
|
+
module OnePassword
|
5
|
+
class Item
|
6
|
+
INDEX_UUID = 0
|
7
|
+
INDEX_TYPE = 1
|
8
|
+
INDEX_NAME = 2
|
9
|
+
INDEX_URL = 3
|
10
|
+
INDEX_DATE = 4
|
11
|
+
INDEX_FOLDER = 5
|
12
|
+
INDEX_PASSWORD_STRENGTH = 6
|
13
|
+
INDEX_TRASHED = 7
|
14
|
+
|
15
|
+
# @return [String]
|
16
|
+
attr_accessor :uuid, :type, :title, :domain, :updated_at, :trashed, :security_level, :open_contents, :encrypted#,
|
17
|
+
#:location_key, :open_contents, :key_id, :location, :encrypted,
|
18
|
+
#:created_at, :trashed, :type_name #,
|
19
|
+
#:reminderq, :port, :notes_plain, :cash_limit, :html_name, :fields,
|
20
|
+
#:expiry_mm, :expiry_yy, :member_name, :zip, :database_type, :html_action, :phone_toll_free,
|
21
|
+
#:lastname, :password, :html_method, :html_id,
|
22
|
+
#:renewal_date_yy, :renewal_date_mm, :renewal_date_dd, :cardholder, :credit_limit,
|
23
|
+
#:birthdate_yy, :birthdate_mm, :birthdate_dd, :username, :company, :bank, :email, :ccnum,
|
24
|
+
#:idisk_storage, :pin, :firstname, :path, :hostname, :website, :country, :interest, :database, :sex,
|
25
|
+
#:server, :cvv, :address1, :address2, :cellphone_local, :city, :aim, :jobtitle, :state, :occupation,
|
26
|
+
#:department, :busphone_local
|
27
|
+
# @return [Time]
|
28
|
+
|
29
|
+
# @return [OnePassword::Profile]
|
30
|
+
attr_reader :profile
|
31
|
+
|
32
|
+
# @param [Profile] profile
|
33
|
+
# @param [Array] data
|
34
|
+
def initialize(profile, data)
|
35
|
+
@profile = profile
|
36
|
+
self.attributes = {
|
37
|
+
uuid: data[INDEX_UUID],
|
38
|
+
type: data[INDEX_TYPE],
|
39
|
+
title: data[INDEX_NAME],
|
40
|
+
domain: data[INDEX_URL],
|
41
|
+
updated_at: data[INDEX_DATE],
|
42
|
+
trashed: data[INDEX_TRASHED]
|
43
|
+
}
|
44
|
+
end
|
45
|
+
|
46
|
+
def updated_at=(seconds)
|
47
|
+
@updated_at = Time.at(seconds)
|
48
|
+
end
|
49
|
+
|
50
|
+
def created_at=(seconds)
|
51
|
+
@created_at = Time.at(seconds)
|
52
|
+
end
|
53
|
+
|
54
|
+
def system?
|
55
|
+
@type =~ /^system/
|
56
|
+
end
|
57
|
+
|
58
|
+
def wallet?
|
59
|
+
@type =~ /^wallet\.(membership|financial|government)}/
|
60
|
+
end
|
61
|
+
|
62
|
+
def trashed?
|
63
|
+
@trashed == 'Y'
|
64
|
+
end
|
65
|
+
|
66
|
+
def category
|
67
|
+
@category ||= case type
|
68
|
+
when SOFTWARE_LICENSES, WEBFORMS, NOTES, IDENTITIES, PASSWORDS
|
69
|
+
type.to_sym
|
70
|
+
else
|
71
|
+
if type == FOLDERS
|
72
|
+
:folders
|
73
|
+
elsif wallet?
|
74
|
+
WALLET
|
75
|
+
else
|
76
|
+
ACCOUNT.to_sym
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def matches?(text)
|
82
|
+
title.downcase.index(text) || domain.downcase.index(text)
|
83
|
+
end
|
84
|
+
|
85
|
+
def file_name
|
86
|
+
profile.directory.join("#{uuid}.1password")
|
87
|
+
end
|
88
|
+
|
89
|
+
def security_level
|
90
|
+
open_contents.try(:[], 'securityLevel') || 'SL5'
|
91
|
+
end
|
92
|
+
|
93
|
+
def encryption_key
|
94
|
+
profile.encryption_keys[security_level].decrypted_key
|
95
|
+
end
|
96
|
+
|
97
|
+
def load_encrypted_data
|
98
|
+
self.attributes = JSON.parse(File.read(file_name))
|
99
|
+
end
|
100
|
+
|
101
|
+
def encrypted
|
102
|
+
load_encrypted_data unless @encrypted
|
103
|
+
@encrypted
|
104
|
+
end
|
105
|
+
|
106
|
+
def decrypt_data
|
107
|
+
unless @decrypted
|
108
|
+
@decrypted = true
|
109
|
+
plain_text = Encryption.decrypt_using_key(encrypted, encryption_key)
|
110
|
+
attrs = JSON.parse(plain_text)
|
111
|
+
self.attributes = attrs
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
def login_username
|
116
|
+
find_field_with_designation('username')
|
117
|
+
end
|
118
|
+
|
119
|
+
def login_password
|
120
|
+
attributes['password'].presence || find_field_with_designation('password')
|
121
|
+
end
|
122
|
+
|
123
|
+
def find_field_with_designation(designation)
|
124
|
+
fields = attributes['fields']
|
125
|
+
field = fields.find do |field|
|
126
|
+
field['designation'] == designation
|
127
|
+
end if fields
|
128
|
+
|
129
|
+
field['value'] if field
|
130
|
+
end
|
131
|
+
|
132
|
+
def attributes
|
133
|
+
@attributes ||= {}
|
134
|
+
fields = instance_variables.inject({}) do |result, ivar|
|
135
|
+
result[ivar] = instance_variable_get(ivar) unless ivar == :@profile
|
136
|
+
result
|
137
|
+
end
|
138
|
+
@attributes.merge(fields)
|
139
|
+
end
|
140
|
+
|
141
|
+
def attributes=(attrs)
|
142
|
+
attrs.each do |name, value|
|
143
|
+
if value.present?
|
144
|
+
attribute = name.to_s.underscore
|
145
|
+
writer = "#{attribute}="
|
146
|
+
if respond_to?(writer)
|
147
|
+
send(writer, value)
|
148
|
+
else
|
149
|
+
@attributes ||= {}
|
150
|
+
@attributes[attribute] = value
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
require 'json'
|
4
|
+
require 'one_password/profile'
|
5
|
+
require 'one_password/errors'
|
6
|
+
|
7
|
+
module OnePassword
|
8
|
+
class Keychain
|
9
|
+
def initialize(directory = '~/Dropbox/1Password.agilekeychain')
|
10
|
+
@directory = Pathname(File.expand_path(directory))
|
11
|
+
@master_password = nil
|
12
|
+
profiles
|
13
|
+
end
|
14
|
+
|
15
|
+
# @return [Profile]
|
16
|
+
def current_profile
|
17
|
+
@current_profile ||= profiles['default']
|
18
|
+
end
|
19
|
+
|
20
|
+
# @param [String, Profile] profile
|
21
|
+
def current_profile=(profile)
|
22
|
+
unless profile.is_a?(Profile)
|
23
|
+
raise UndefinedProfile.new(profile) unless profiles.key?(profile)
|
24
|
+
profile = profiles[profile]
|
25
|
+
end
|
26
|
+
@profile = profile
|
27
|
+
end
|
28
|
+
|
29
|
+
def password=(password)
|
30
|
+
current_profile.password = password
|
31
|
+
end
|
32
|
+
|
33
|
+
protected
|
34
|
+
|
35
|
+
def data_directory
|
36
|
+
@directory.join('data')
|
37
|
+
end
|
38
|
+
|
39
|
+
def profiles
|
40
|
+
@profiles ||= Dir["#{data_directory}/*"].inject({}) do |result, dir|
|
41
|
+
profile = Profile.new(self, dir)
|
42
|
+
result[profile.name] = profile
|
43
|
+
result
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|