Class: Keybox::Storage::Container

Inherits:
Record
  • Object
show all
Defined in:
lib/keybox/storage/container.rb

Overview

The container of the Keybox records. The Container itself is a Keybox::Storage::Record with a few extra methods.

A instance of a Container is created with a passphrase and a path to a file. The passphrase can be anything that has a to_s method.

container = Keybox::Storage::Container.new("i love ruby", "/tmp/database.yml")

This will load from the given file with the given passphrase if the file exists, or it will initialize the container to accept records.

The records are held decrypted in memory, so keep that in mind if that is a concern.

Add Records

record = Keybox::Storage::Record.new
record.field1 = "data"
record.field2 = "some more data"

container << record

Delete Records

container.delete(record)

There is no ‘update’ record, just delete it and add it.

Find a record accepts a string and will look in all the records it contains for anything that matches it. It coerces strings into Regexp so any regex can be used here too.

container.find("data")

Report if the container or any of its records have been modified:

container.modified?

Save the container to its default location:

container.save

Or to some other location

container.save("/some/other/path.yml")

Direct access to the decrypted records is also available through the records accessor.

container.records #=> Array of Keybox::Storage::Record

Constant Summary collapse

ITERATIONS =
2048
MINIMUM_PHRASE_LENGTH =
4

Constants inherited from Record

Record::PROTECTED_METHODS

Instance Attribute Summary collapse

Attributes inherited from Record

#creation_time, #data_members, #last_access_time, #modification_time, #uuid

Instance Method Summary collapse

Methods inherited from Record

#==, #data_member_names, #eql?, #method_missing, #modified=, #to_yaml_properties

Constructor Details

#initialize(passphrase, path) ⇒ Container

Returns a new instance of Container.



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
# File 'lib/keybox/storage/container.rb', line 73

def initialize(passphrase,path)
    super()

    @path        = path
    @passphrase  = passphrase
    @records     = []

    if not load_from_file then
        strength_check(@passphrase)
        self.version                    = Keybox::VERSION
        
        self.key_calc_iterations        = ITERATIONS
        self.key_digest_salt            = Keybox::RandomDevice.random_bytes(32)
        self.key_digest_algorithm       = Keybox::Digest::DEFAULT_ALGORITHM
        self.key_digest                 = calculated_key_digest(passphrase)

        self.records_init_vector        = Keybox::RandomDevice.random_bytes(16)
        self.records_cipher_algorithm   = Keybox::Cipher::DEFAULT_ALGORITHM

        self.records_encrypted_data     = ""
        self.records_digest_salt        = Keybox::RandomDevice.random_bytes(32)
        self.records_digest_algorithm   = Keybox::Digest::DEFAULT_ALGORITHM
        self.records_digest             = ""
    end
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method in the class Keybox::Storage::Record

Instance Attribute Details

#recordsObject (readonly)

Returns the value of attribute records.



69
70
71
# File 'lib/keybox/storage/container.rb', line 69

def records
  @records
end

Instance Method Details

#<<(obj) ⇒ Object

Add a record to the system



170
171
172
173
174
175
# File 'lib/keybox/storage/container.rb', line 170

def <<(obj)
    if obj.respond_to?("needs_container_passphrase?") and obj.needs_container_passphrase? then
        obj.container_passphrase = @passphrase
    end
    @records << obj
end

#calculated_key(passphrase = @passphrase) ⇒ Object

calculate the encryption key from the initial passphrase



152
153
154
155
156
157
158
# File 'lib/keybox/storage/container.rb', line 152

def calculated_key(passphrase = @passphrase)
    key = self.key_digest_salt + passphrase
    self.key_calc_iterations.times do 
        key = Keybox::Digest::CLASSES[self.key_digest_algorithm].digest(key)
    end
    return key
end

#calculated_key_digest(passphrase) ⇒ Object

calculate the key digest of the encryption key



163
164
165
# File 'lib/keybox/storage/container.rb', line 163

def calculated_key_digest(passphrase)
    Keybox::Digest::CLASSES[self.key_digest_algorithm].hexdigest(calculated_key(passphrase))
end

#delete(obj) ⇒ Object

Delete a record from the system, we force a modified flag here since the underlying Record wasn’t ‘assigned to’ we have to force modification notification.



196
197
198
199
# File 'lib/keybox/storage/container.rb', line 196

def delete(obj)
    @records.delete(obj)
    @modified = true
end

#find(search_string, restricted_to = nil) ⇒ Object

Search all the records in the database finding any that match the search string passed in. The Search string is converted to a Regexp before beginning.

A list of restricted fields can also be passed in and the regexp will only be matched against those fields



216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
# File 'lib/keybox/storage/container.rb', line 216

def find(search_string,restricted_to = nil)
    case search_string
    when Regexp
        regex = Regexp.new(search_string.source,search_string.options | Regexp::IGNORECASE)
    else
        regex = Regexp.new(search_string.to_s,Regexp::IGNORECASE)
    end
    matches = []
    @records.each do |record|
        restricted_to = restricted_to || ( record.data_member_names - %w(password) )
        record.data_members.each_pair do |k,v|
            if regex.match(v) and restricted_to.include?(k.to_s) then
                matches << record
                break
            end
        end
    end
    return matches
end

#find_by_url(url) ⇒ Object

Search only records that have a url field



204
205
206
# File 'lib/keybox/storage/container.rb', line 204

def find_by_url(url)
    find(url,%w(url))
end

#lengthObject

The number of Records in the Container



180
181
182
# File 'lib/keybox/storage/container.rb', line 180

def length
    @records.size
end

#load_from_fileObject

load from file, if this is successful then replace the existing member fields on this instance with the data from the file



116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
# File 'lib/keybox/storage/container.rb', line 116

def load_from_file
    return false unless File.exists?(@path)
    return false unless tmp = YAML.load_file(@path)
    @creation_time      = tmp.creation_time
    @modification_time  = tmp.modification_time
    @last_access_time   = tmp.last_access_time
    @data_members       = tmp.data_members
    @uuid               = tmp.uuid
    validate_passphrase
    decrypt_records
    validate_decryption
    load_records
    @modified = false
    true
end

#modified?Boolean

See if we are modified, or if any of the records are modified

Returns:

  • (Boolean)


240
241
242
243
244
245
246
# File 'lib/keybox/storage/container.rb', line 240

def modified?
    return true if super
    @records.each do |record|
        return true if record.modified?
    end
    return false
end

#passphrase=(new_passphrase) ⇒ Object

Change the master password of the container



102
103
104
105
106
107
108
109
# File 'lib/keybox/storage/container.rb', line 102

def passphrase=(new_passphrase)
    strength_check(new_passphrase)
    @passphrase                 = new_passphrase
    self.key_digest_salt        = Keybox::RandomDevice.random_bytes(32)
    self.key_digest             = calculated_key_digest(new_passphrase)
    self.records_init_vector    = Keybox::RandomDevice.random_bytes(16)
    self.records_digest_salt    = Keybox::RandomDevice.random_bytes(32)
end

#save(path = @path) ⇒ Object

save the current container to a file



135
136
137
138
139
140
141
142
143
144
145
146
147
# File 'lib/keybox/storage/container.rb', line 135

def save(path = @path)
    calculate_records_digest 
    encrypt_records
    File.open(path,"w") do |f|
        f.write(self.to_yaml)
    end

    # mark everything as not modified
    @records.each do |record|
        record.modified = false
    end
    @modified = false
end

#sizeObject

The number of Records in the Container



187
188
189
# File 'lib/keybox/storage/container.rb', line 187

def size
    @records.size
end