Class: OnePass::OpVault
- Inherits:
-
Object
- Object
- OnePass::OpVault
- Defined in:
- lib/OnePass/op_vault.rb
Overview
Handle all the 1Password OpVault operations
Constant Summary collapse
- FIELD_TYPES =
{ password: 'P', text: 'T', email: 'E', number: 'N', radio: 'R', telephone: 'TEL', checkbox: 'C', url: 'U' }.freeze
- DESIGNATION_TYPES =
{ username: 'username', password: 'password', none: '' }.freeze
Instance Method Summary collapse
- #check_and_set_profile(profile_path) ⇒ Object
- #check_and_set_vault(vault_path) ⇒ Object
- #check_hmac(data, hmac_key, desired_hmac) ⇒ Object
- #decrypt_data(key, iv, data) ⇒ Object
- #decrypt_keys(encrypted_key, derived_key, derived_mac_key) ⇒ Object
- #decrypt_opdata(cipher_text, cipher_key, cipher_mac_key) ⇒ Object
- #derive_keys(master_password, salt, iterations) ⇒ Object
- #find(search) ⇒ Object
-
#initialize(vault_path) ⇒ OpVault
constructor
A new instance of OpVault.
- #item_detail(item) ⇒ Object
- #item_keys(item) ⇒ Object
- #item_overview(item) ⇒ Object
- #load_items ⇒ Object
- #lock ⇒ Object
- #master_keys(derived_key, derived_mac_key) ⇒ Object
- #overview_keys(derived_key, derived_mac_key) ⇒ Object
- #unlock(master_password = nil) ⇒ Object
- #unlocked? ⇒ Boolean
Constructor Details
#initialize(vault_path) ⇒ OpVault
Returns a new instance of OpVault.
26 27 28 29 30 31 |
# File 'lib/OnePass/op_vault.rb', line 26 def initialize(vault_path) check_and_set_vault vault_path check_and_set_profile File.join(@vault_path, 'default', 'profile.js') @items = {} @item_index = {} end |
Instance Method Details
#check_and_set_profile(profile_path) ⇒ Object
40 41 42 43 44 45 46 47 48 49 50 51 52 53 |
# File 'lib/OnePass/op_vault.rb', line 40 def check_and_set_profile(profile_path) unless File.exist? profile_path raise ArgumentError.new, 'Vault profile does not exist' end profile = File.read profile_path unless profile.start_with?('var profile=') && profile.end_with?(';') raise 'Vault profile format incorrect' end @profile = JSON.parse profile[12..-2] rescue raise 'Unable to parse vault profile' end |
#check_and_set_vault(vault_path) ⇒ Object
33 34 35 36 37 38 |
# File 'lib/OnePass/op_vault.rb', line 33 def check_and_set_vault(vault_path) @vault_path = File.(vault_path) unless File.exist? @vault_path raise ArgumentError.new, 'Vault file does not exist' end end |
#check_hmac(data, hmac_key, desired_hmac) ⇒ Object
75 76 77 78 79 |
# File 'lib/OnePass/op_vault.rb', line 75 def check_hmac(data, hmac_key, desired_hmac) digest = OpenSSL::Digest::SHA256.new computed_hmac = OpenSSL::HMAC.digest digest, hmac_key, data raise ArgumentError.new, 'Invalid HMAC' if computed_hmac != desired_hmac end |
#decrypt_data(key, iv, data) ⇒ Object
98 99 100 101 102 103 104 |
# File 'lib/OnePass/op_vault.rb', line 98 def decrypt_data(key, iv, data) cipher = OpenSSL::Cipher::AES256.new(:CBC).decrypt cipher.key = key cipher.iv = iv cipher.padding = 0 cipher.update(data) + cipher.final end |
#decrypt_keys(encrypted_key, derived_key, derived_mac_key) ⇒ Object
137 138 139 140 141 142 143 |
# File 'lib/OnePass/op_vault.rb', line 137 def decrypt_keys(encrypted_key, derived_key, derived_mac_key) key_base = decrypt_opdata encrypted_key, derived_key, derived_mac_key keys = OpenSSL::Digest::SHA512.new.digest key_base # => key, hmac [keys[0..31], keys[32..63]] end |
#decrypt_opdata(cipher_text, cipher_key, cipher_mac_key) ⇒ Object
106 107 108 109 110 111 112 113 114 115 |
# File 'lib/OnePass/op_vault.rb', line 106 def decrypt_opdata(cipher_text, cipher_key, cipher_mac_key) key_data = cipher_text[0..-33] mac_data = cipher_text[-32..-1] check_hmac key_data, cipher_mac_key, mac_data plaintext = decrypt_data cipher_key, key_data[16..31], key_data[32..-1] plaintext_size = key_data[8..15].unpack('Q')[0] plaintext[-plaintext_size..-1] end |
#derive_keys(master_password, salt, iterations) ⇒ Object
117 118 119 120 121 122 123 124 125 |
# File 'lib/OnePass/op_vault.rb', line 117 def derive_keys(master_password, salt, iterations) digest = OpenSSL::Digest::SHA512.new derived_key = OpenSSL::PKCS5.pbkdf2_hmac( master_password, salt, iterations, digest.digest_length, digest ) # => key, hmac [derived_key[0..31], derived_key[32..63]] end |
#find(search) ⇒ Object
94 95 96 |
# File 'lib/OnePass/op_vault.rb', line 94 def find(search) @items.values_at *@item_index.values_at(*@item_index.keys.grep(search)) end |
#item_detail(item) ⇒ Object
163 164 165 166 167 168 |
# File 'lib/OnePass/op_vault.rb', line 163 def item_detail(item) data = Base64.decode64(item['d']) item_key, item_mac_key = item_keys item detail = decrypt_opdata data, item_key, item_mac_key { 'uuid' => item['uuid'] }.merge JSON.parse detail end |
#item_keys(item) ⇒ Object
145 146 147 148 149 150 151 152 153 154 155 |
# File 'lib/OnePass/op_vault.rb', line 145 def item_keys(item) item_key = Base64.decode64 item['k'] key_data = item_key[0..-33] key_hmac = item_key[-32..-1] check_hmac key_data, @master_mac_key, key_hmac plaintext = decrypt_data @master_key, key_data[0..15], key_data[16..-1] # => key, hmac [plaintext[0..31], plaintext[32..63]] end |
#item_overview(item) ⇒ Object
157 158 159 160 161 |
# File 'lib/OnePass/op_vault.rb', line 157 def item_overview(item) data = Base64.decode64(item['o']) overview = decrypt_opdata data, @overview_key, @overview_mac_key { 'uuid' => item['uuid'] }.merge JSON.parse overview end |
#load_items ⇒ Object
81 82 83 84 85 86 87 88 89 90 91 92 |
# File 'lib/OnePass/op_vault.rb', line 81 def load_items file_glob = File.join(@vault_path, 'default', 'band_*.js') Dir.glob(file_glob) do |file| band = JSON.parse File.read(file)[3..-3] @items.merge! band end @items.each_pair do |uuid, item| overview = item_overview item @item_index[overview['title']] = uuid end end |
#lock ⇒ Object
71 72 73 |
# File 'lib/OnePass/op_vault.rb', line 71 def lock @master_key = @master_mac_key = nil end |
#master_keys(derived_key, derived_mac_key) ⇒ Object
127 128 129 130 |
# File 'lib/OnePass/op_vault.rb', line 127 def master_keys(derived_key, derived_mac_key) encrypted = Base64.decode64(@profile['masterKey']) decrypt_keys encrypted, derived_key, derived_mac_key end |
#overview_keys(derived_key, derived_mac_key) ⇒ Object
132 133 134 135 |
# File 'lib/OnePass/op_vault.rb', line 132 def overview_keys(derived_key, derived_mac_key) encrypted = Base64.decode64(@profile['overviewKey']) decrypt_keys encrypted, derived_key, derived_mac_key end |
#unlock(master_password = nil) ⇒ Object
55 56 57 58 59 60 61 62 63 64 65 |
# File 'lib/OnePass/op_vault.rb', line 55 def unlock(master_password = nil) master_password = yield if block_given? salt = Base64.decode64(@profile['salt']) iterations = @profile['iterations'] key, mac_key = derive_keys master_password, salt, iterations @master_key, @master_mac_key = master_keys key, mac_key @overview_key, @overview_mac_key = overview_keys key, mac_key rescue raise 'Incorrect password' end |
#unlocked? ⇒ Boolean
67 68 69 |
# File 'lib/OnePass/op_vault.rb', line 67 def unlocked? !!@master_key && !!@overview_key end |