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
- #master_key_load ⇒ 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() @options = @options[:cipher] ||= 'AES-256-CTR' @options[:keysize] ||= 2048 @options[:iterations] ||= 2000 @options[:digest] ||= 'sha512' unless @options[:path].class == Pathname @options[:path] = Pathname.new(@options[: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
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 |
# File 'lib/pwkeep/storage.rb', line 73 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 @options[: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
158 159 160 161 162 163 164 165 |
# File 'lib/pwkeep/storage.rb', line 158 def delete(system) data = load_system(system) unless data[:system] == system raise "System not found" end path.join(system_to_hash(system)).delete! end |
#encrypt_system(file, data) ⇒ Object
100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 |
# File 'lib/pwkeep/storage.rb', line 100 def encrypt_system(file, data) unless @key raise PWKeep::Exception, "Private key required" end # encrypt data cipher = OpenSSL::Cipher::Cipher.new @options[: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 @options[:keysize] cipher = OpenSSL::Cipher.new @options[: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
167 168 169 170 171 172 173 174 |
# File 'lib/pwkeep/storage.rb', line 167 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
125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 |
# File 'lib/pwkeep/storage.rb', line 125 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 |
#master_key_load ⇒ Object
53 54 55 56 57 58 59 60 |
# File 'lib/pwkeep/storage.rb', line 53 def master_key_load unless @key raise PWKeep::Exception, "RSA private key required" end # load the key @master_key = @key.private_decrypt(path.join('master.key').open('rb') { |io| io.read },4) end |
#path ⇒ Object
9 10 11 |
# File 'lib/pwkeep/storage.rb', line 9 def path @options[:path] end |
#save_system(system, data) ⇒ Object
142 143 144 145 146 147 148 149 150 151 152 |
# File 'lib/pwkeep/storage.rb', line 142 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) end |
#system_to_hash(system) ⇒ Object
62 63 64 65 66 67 68 69 70 71 |
# File 'lib/pwkeep/storage.rb', line 62 def system_to_hash(system) d = Digest.const_get(@options[:digest].upcase).new system_h = system.downcase (0..@options[:iterations]).each do system_h = d.update(system_h).digest d.reset end "system-#{Base64.urlsafe_encode64(system_h)}" end |
#valid? ⇒ Boolean
154 155 156 |
# File 'lib/pwkeep/storage.rb', line 154 def valid? path.join('private.pem').exist? end |