Class: PWKeep::Storage
- Inherits:
-
Object
- Object
- PWKeep::Storage
- Defined in:
- lib/pwkeep/storage.rb
Instance Method Summary collapse
- #create ⇒ Object
- #decrypt_system(file) ⇒ Object
- #delete(system) ⇒ Object
- #encrypt_system(file, data) ⇒ Object
-
#initialize(options) ⇒ Storage
constructor
A new instance of Storage.
- #keypair_create(password) ⇒ Object
- #keypair_load(password) ⇒ Object
- #list_all_systems ⇒ Object
- #load_system(system) ⇒ Object
- #migrate ⇒ Object
- #path ⇒ Object
- #save_system(system, data) ⇒ Object
- #system_to_hash(system) ⇒ Object
- #valid? ⇒ Boolean
Constructor Details
#initialize(options) ⇒ Storage
Returns a new instance of Storage.
13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
# File 'lib/pwkeep/storage.rb', line 13 def initialize() = [:cipher] ||= 'AES-256-CTR' [:keysize] ||= 2048 [:iterations] ||= 2000 [:digest] ||= 'sha512' unless [:path].class == Pathname [:path] = Pathname.new([:path].to_s). end if path.exist? @lockfile = Lockfile.new path.join(".lock").to_s, :retries => 0 @lockfile.lock ObjectSpace.define_finalizer(self, proc { @lockfile.unlock }) end end |
Instance Method Details
#create ⇒ Object
32 33 34 35 36 37 38 |
# File 'lib/pwkeep/storage.rb', line 32 def create return if path.exist? path.mkdir @lockfile = Lockfile.new path.join(".lock").to_s, :retries => 0 @lockfile.lock ObjectSpace.define_finalizer(self, proc { @lockfile.unlock }) end |
#decrypt_system(file) ⇒ Object
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 |
# File 'lib/pwkeep/storage.rb', line 67 def decrypt_system(file) unless @key raise PWKeep::Exception, "Private key required" end # found it, decrypt and load json # the file contains crypto name, iv len, iv, data header = nil data = nil file.open('rb') { |io| header = io.read [:keysize]/8 data = io.read } # header cipher = @key.private_decrypt(header,4).unpack('Z*')[0] cipher = OpenSSL::Cipher.new cipher # re-unpack now that we know the size of the rest of the fields... header = @key.private_decrypt(header,4).unpack("Z*a#{cipher.iv_len}a#{cipher.key_len}") cipher.decrypt cipher.iv = header[1] cipher.key = header[2] # perform decrypt cipher.update(data) + cipher.final end |
#delete(system) ⇒ Object
152 153 154 155 156 157 158 159 |
# File 'lib/pwkeep/storage.rb', line 152 def delete(system) data = load_system(system) unless data[:system] == system raise PWKeep::Exception, "System not found" end path.join(system_to_hash(system)).delete! end |
#encrypt_system(file, data) ⇒ Object
94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 |
# File 'lib/pwkeep/storage.rb', line 94 def encrypt_system(file, data) unless @key raise PWKeep::Exception, "Private key required" end # encrypt data cipher = OpenSSL::Cipher::Cipher.new [:cipher] cipher.encrypt # use one time key and iv iv = cipher.random_iv key = cipher.random_key header = [cipher.name, iv, key].pack("Z*a#{cipher.iv_len}a#{cipher.key_len}") blob = cipher.update(data) + cipher.final # store system name to make search work file.open('wb') do |io| io.write @key.public_encrypt header, 4 io.write blob end true end |
#keypair_create(password) ⇒ Object
40 41 42 43 44 45 46 |
# File 'lib/pwkeep/storage.rb', line 40 def keypair_create(password) # ensure it does not exist @key = OpenSSL::PKey::RSA.new [:keysize] cipher = OpenSSL::Cipher.new [:keycipher] path.join('private.pem').open 'w' do |io| io.write @key.export(cipher, password) end end |
#keypair_load(password) ⇒ Object
48 49 50 51 |
# File 'lib/pwkeep/storage.rb', line 48 def keypair_load(password) key_pem = path.join('private.pem').read @key = OpenSSL::PKey::RSA.new key_pem, password end |
#list_all_systems ⇒ Object
161 162 163 164 165 166 167 168 |
# File 'lib/pwkeep/storage.rb', line 161 def list_all_systems systems = [] path.entries.each do |s| next unless s.fnmatch? "system-*" systems << JSON.load(decrypt_system(path.join(s)))["system"] end systems end |
#load_system(system) ⇒ Object
119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 |
# File 'lib/pwkeep/storage.rb', line 119 def load_system(system) unless @key raise PWKeep::Exception, "Private key required" end system_h = system_to_hash(system) raise "Cannot find #{system}" unless path.join(system_h).exist? data = decrypt_system(path.join(system_h)) unless data[0] == "{" and data[-1] == "}" raise PWKeep::Exception, "Corrupted data file" end JSON.load(data).deep_symbolize_keys end |
#migrate ⇒ Object
170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 |
# File 'lib/pwkeep/storage.rb', line 170 def migrate count = 0 path.entries.each do |s| next unless s.fnmatch? "system-*" # check whether name matches the system name system = JSON.load(decrypt_system(path.join(s)))["system"] system_h = system_to_hash system if s.to_s != system_h count = count + 1 File.rename path.join(s), path.join(system_h) end end count end |
#path ⇒ Object
9 10 11 |
# File 'lib/pwkeep/storage.rb', line 9 def path [:path] end |
#save_system(system, data) ⇒ Object
136 137 138 139 140 141 142 143 144 145 146 |
# File 'lib/pwkeep/storage.rb', line 136 def save_system(system, data) unless @key raise PWKeep::Exception, "Private key required" end # write system system_h = system_to_hash(system) data = { :system => system, :data => data, :stored_at => Time.now } encrypt_system(path.join(system_h), data.to_json) end |
#system_to_hash(system) ⇒ Object
53 54 55 56 57 58 59 60 61 62 63 64 65 |
# File 'lib/pwkeep/storage.rb', line 53 def system_to_hash(system) d = Digest.const_get([:digest].upcase).new # hash with public key to prevent dictionary attacks system_h = system.downcase + @key.public_key.to_der (0..[:iterations]).each do system_h = d.update(system_h).digest d.reset end "system-#{Base64.urlsafe_encode64(system_h)}" end |
#valid? ⇒ Boolean
148 149 150 |
# File 'lib/pwkeep/storage.rb', line 148 def valid? path.join('private.pem').exist? end |