Class: Syde::Vault

Inherits:
Object
  • Object
show all
Includes:
Errors
Defined in:
lib/syde/vault.rb,
lib/syde/crypto.rb,
lib/syde/storage.rb

Defined Under Namespace

Modules: Crypto, Storage

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(data, file) ⇒ Vault

Returns a new instance of Vault.

Raises:

  • (ArgumentError)


44
45
46
47
48
49
50
# File 'lib/syde/vault.rb', line 44

def initialize(data, file)
	@data = data
	@file = file.freeze
			
	raise ArgumentError, "unable to find any stored data." if @data.empty?
	raise ArgumentError, "data is not valid." unless Storage.valid_format?(@data)
end

Instance Attribute Details

#fileObject (readonly)

Returns the value of attribute file.



6
7
8
# File 'lib/syde/vault.rb', line 6

def file
  @file
end

#plaintext_secret_keyObject

Returns the value of attribute plaintext_secret_key.



5
6
7
# File 'lib/syde/vault.rb', line 5

def plaintext_secret_key
  @plaintext_secret_key
end

Class Method Details

.create(password, file = Storage::DefaultStorageFile) ⇒ Object



15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
# File 'lib/syde/vault.rb', line 15

def self.create(password, file = Storage::DefaultStorageFile)
  file = File.expand_path(file)
  
	raise "#{file} contains content -- refusing to override." if File.exist?(file) && File.size(file) > 0
	
	FileUtils.touch(file) unless File.exist?(file)
	
	h = {}
	[:plaintext, :encrypted].each { |e| h[e] = {} }
	
	h[:plaintext][:iv] = Crypto.new_iv
	new_secret_key = Vault.new_secret_key(password, h[:plaintext][:iv])
	encrypted_key = new_secret_key[:encrypted_key]
	hash = new_secret_key[:plaintext_key_hash]
	plaintext_key = new_secret_key[:plaintext_key]
	
	h[:encrypted][:secret_key] = encrypted_key
	h[:plaintext][:secret_key_hash] = hash
	
	h[:encrypted][:contents] = Crypto.encrypt(plaintext_key, h[:plaintext][:iv], YAML.dump([]))
	h[:plaintext][:contents] = []
			
	File.open(file, "w") do |f|
		f << YAML.dump(h)
	end
	
	Vault.new(h, file)
end

.new_secret_key(password, iv) ⇒ Object



204
205
206
207
208
209
210
211
212
# File 'lib/syde/vault.rb', line 204

def self.new_secret_key(password, iv)
	plaintext = Crypto.digest(Crypto.random_bytes(4096))
	new_key = Crypto.aes(:encrypt, password, iv, plaintext)
	#? plaintext = nil
	#? GC.start
	{ :encrypted_key => new_key,
	  :plaintext_key_hash => Crypto.digest(plaintext),
	  :plaintext_key => plaintext	}
end

.open(file = Storage::DefaultStorageFile) ⇒ Object



8
9
10
11
12
13
# File 'lib/syde/vault.rb', line 8

def self.open(file = Storage::DefaultStorageFile)
  file = File.expand_path(file)
  FileUtils.touch(file) unless File.exist?(file)
  
	Vault.new(YAML.load_file(file) || "", file)
end

Instance Method Details

#add(*contents) ⇒ Object Also known as: <<

Raises:



175
176
177
178
179
180
181
182
183
# File 'lib/syde/vault.rb', line 175

def add(*contents)
  raise AccessError, "vault is locked; unable to add content to vault." if locked?
  
  contents.each do |content|
    internal_contents << content
  end

   update_contents(internal_contents)
end

#contentsObject



141
142
143
144
145
146
147
# File 'lib/syde/vault.rb', line 141

def contents
  if locked?
    public_contents
  else
    plaintext_contents
  end
end

#contents=(new_content) ⇒ Object

Raises:



167
168
169
170
171
172
173
# File 'lib/syde/vault.rb', line 167

def contents=(new_content)
  raise AccessError, "vault is locked; unable to modify vault contents." if locked?
  
  internal_contents.replace(new_content)
  
  update_contents(new_content)
end

#dataObject



52
53
54
55
56
57
58
# File 'lib/syde/vault.rb', line 52

def data
   if locked?
     public_data
   else
     internal_data
   end
end

#decrypt_secret_key(password) ⇒ Object



82
83
84
# File 'lib/syde/vault.rb', line 82

def decrypt_secret_key(password)
	Crypto.aes(:decrypt, password, iv, internal_data[:encrypted][:secret_key])
end

#inspectObject



200
201
202
# File 'lib/syde/vault.rb', line 200

def inspect
	%Q{#<Vault (#{status})>}
end

#ivObject



74
75
76
# File 'lib/syde/vault.rb', line 74

def iv
	internal_data[:plaintext][:iv]
end

#lockObject



86
87
88
89
90
# File 'lib/syde/vault.rb', line 86

def lock
  internal_data[:encrypted][:contents] = Crypto.encrypt(@plaintext_secret_key, iv, YAML.dump(internal_data[:plaintext][:contents]))
  internal_data[:plaintext][:contents] = nil
	@plaintext_secret_key = nil
end

#locked?Boolean

Returns:

  • (Boolean)


119
120
121
122
123
124
125
# File 'lib/syde/vault.rb', line 119

def locked?
	if plaintext_secret_key
		false
	else
		true
	end
end

#plaintext_contentsObject

Raises:



127
128
129
130
131
# File 'lib/syde/vault.rb', line 127

def plaintext_contents
  raise AccessError, "vault is locked; unable to access vault contents." if locked?
  
   YAML.load(YAML.dump(internal_contents))
end

#public_contentsObject

Raises:



149
150
151
152
153
# File 'lib/syde/vault.rb', line 149

def public_contents
  raise AccessError, "vault is locked; unable to access vault contents." if locked?
  
  public_data[:plaintext][:contents]
end

#public_dataObject



60
61
62
63
64
# File 'lib/syde/vault.rb', line 60

def public_data
  public_data = YAML.load(YAML.dump(internal_data))
  public_data[:plaintext].delete(:contents)
  public_data
end

#remove(*contents) ⇒ Object

Raises:



186
187
188
189
190
191
192
193
194
# File 'lib/syde/vault.rb', line 186

def remove(*contents)
  raise AccessError, "vault is locked; unable to remove content from vault." if locked?
  
  contents.each do |content|
    internal_contents.delete(content)
  end
  
  update_contents(internal_contents)
end

#secret_key_hashObject



78
79
80
# File 'lib/syde/vault.rb', line 78

def secret_key_hash
	internal_data[:plaintext][:secret_key_hash]
end

#start_locking_timer(seconds) ⇒ Object



112
113
114
115
116
117
# File 'lib/syde/vault.rb', line 112

def start_locking_timer(seconds)
  Thread.new do
    sleep seconds
    self.lock
  end
end

#statusObject



196
197
198
# File 'lib/syde/vault.rb', line 196

def status
	locked? ? "locked" : "unlocked"
end

#unlock(password = nil, timeout = 5 * 60) ⇒ Object



105
106
107
108
109
110
# File 'lib/syde/vault.rb', line 105

def unlock(password = nil, timeout = 5 * 60)
  return false unless timeout > 0
  unlock!(password)
   start_locking_timer(timeout)
	true
end

#unlock!(password = nil) ⇒ Object



92
93
94
95
96
97
98
99
100
101
102
103
# File 'lib/syde/vault.rb', line 92

def unlock!(password = nil)
 	raise MissingPasswordError, "no password given." unless password

 	plaintext_secret_key = decrypt_secret_key(password)
 	if Crypto.digest(plaintext_secret_key) != secret_key_hash
 		raise PasswordIncorrectError
 	else
 		@plaintext_secret_key = plaintext_secret_key
 		internal_data[:encrypted][:contents] ||= Crypto.encrypt(@plaintext_secret_key, iv, YAML.dump([]))
 		internal_data[:plaintext][:contents] = YAML.load(Crypto.decrypt(@plaintext_secret_key, iv, internal_data[:encrypted][:contents]))
 	end
end