safedb 0.01.0001

Sign up to get free protection for your applications and to get access to all the features.
Files changed (90) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +8 -0
  3. data/.yardopts +3 -0
  4. data/Gemfile +10 -0
  5. data/LICENSE +21 -0
  6. data/README.md +793 -0
  7. data/Rakefile +16 -0
  8. data/bin/safe +5 -0
  9. data/lib/configs/README.md +58 -0
  10. data/lib/extension/array.rb +162 -0
  11. data/lib/extension/dir.rb +35 -0
  12. data/lib/extension/file.rb +123 -0
  13. data/lib/extension/hash.rb +33 -0
  14. data/lib/extension/string.rb +572 -0
  15. data/lib/factbase/facts.safedb.net.ini +38 -0
  16. data/lib/interprete.rb +462 -0
  17. data/lib/keytools/PRODUCE_RAND_SEQ_USING_DEV_URANDOM.txt +0 -0
  18. data/lib/keytools/kdf.api.rb +243 -0
  19. data/lib/keytools/kdf.bcrypt.rb +265 -0
  20. data/lib/keytools/kdf.pbkdf2.rb +262 -0
  21. data/lib/keytools/kdf.scrypt.rb +190 -0
  22. data/lib/keytools/key.64.rb +326 -0
  23. data/lib/keytools/key.algo.rb +109 -0
  24. data/lib/keytools/key.api.rb +1391 -0
  25. data/lib/keytools/key.db.rb +330 -0
  26. data/lib/keytools/key.docs.rb +195 -0
  27. data/lib/keytools/key.error.rb +110 -0
  28. data/lib/keytools/key.id.rb +271 -0
  29. data/lib/keytools/key.ident.rb +243 -0
  30. data/lib/keytools/key.iv.rb +107 -0
  31. data/lib/keytools/key.local.rb +259 -0
  32. data/lib/keytools/key.now.rb +402 -0
  33. data/lib/keytools/key.pair.rb +259 -0
  34. data/lib/keytools/key.pass.rb +120 -0
  35. data/lib/keytools/key.rb +585 -0
  36. data/lib/logging/gem.logging.rb +132 -0
  37. data/lib/modules/README.md +43 -0
  38. data/lib/modules/cryptology/aes-256.rb +154 -0
  39. data/lib/modules/cryptology/amalgam.rb +70 -0
  40. data/lib/modules/cryptology/blowfish.rb +130 -0
  41. data/lib/modules/cryptology/cipher.rb +207 -0
  42. data/lib/modules/cryptology/collect.rb +138 -0
  43. data/lib/modules/cryptology/crypt.io.rb +225 -0
  44. data/lib/modules/cryptology/engineer.rb +99 -0
  45. data/lib/modules/mappers/dictionary.rb +288 -0
  46. data/lib/modules/storage/coldstore.rb +186 -0
  47. data/lib/modules/storage/git.store.rb +399 -0
  48. data/lib/session/fact.finder.rb +334 -0
  49. data/lib/session/require.gem.rb +112 -0
  50. data/lib/session/time.stamp.rb +340 -0
  51. data/lib/session/user.home.rb +49 -0
  52. data/lib/usecase/cmd.rb +487 -0
  53. data/lib/usecase/config/README.md +57 -0
  54. data/lib/usecase/docker/README.md +146 -0
  55. data/lib/usecase/docker/docker.rb +49 -0
  56. data/lib/usecase/edit/README.md +43 -0
  57. data/lib/usecase/edit/delete.rb +46 -0
  58. data/lib/usecase/export.rb +40 -0
  59. data/lib/usecase/files/README.md +37 -0
  60. data/lib/usecase/files/eject.rb +56 -0
  61. data/lib/usecase/files/file_me.rb +78 -0
  62. data/lib/usecase/files/read.rb +169 -0
  63. data/lib/usecase/files/write.rb +89 -0
  64. data/lib/usecase/goto.rb +57 -0
  65. data/lib/usecase/id.rb +36 -0
  66. data/lib/usecase/import.rb +157 -0
  67. data/lib/usecase/init.rb +63 -0
  68. data/lib/usecase/jenkins/README.md +146 -0
  69. data/lib/usecase/jenkins/jenkins.rb +208 -0
  70. data/lib/usecase/login.rb +71 -0
  71. data/lib/usecase/logout.rb +28 -0
  72. data/lib/usecase/open.rb +71 -0
  73. data/lib/usecase/print.rb +40 -0
  74. data/lib/usecase/put.rb +81 -0
  75. data/lib/usecase/set.rb +44 -0
  76. data/lib/usecase/show.rb +138 -0
  77. data/lib/usecase/terraform/README.md +91 -0
  78. data/lib/usecase/terraform/terraform.rb +121 -0
  79. data/lib/usecase/token.rb +35 -0
  80. data/lib/usecase/update/README.md +55 -0
  81. data/lib/usecase/update/rename.rb +180 -0
  82. data/lib/usecase/use.rb +41 -0
  83. data/lib/usecase/verse.rb +20 -0
  84. data/lib/usecase/view.rb +71 -0
  85. data/lib/usecase/vpn/README.md +150 -0
  86. data/lib/usecase/vpn/vpn.ini +31 -0
  87. data/lib/usecase/vpn/vpn.rb +54 -0
  88. data/lib/version.rb +3 -0
  89. data/safedb.gemspec +34 -0
  90. metadata +193 -0
@@ -0,0 +1,330 @@
1
+ #!/usr/bin/ruby
2
+ # coding: utf-8
3
+
4
+ module SafeDb
5
+
6
+ require 'json'
7
+
8
+ # A Key/Value database knows how to manipulate a JSON backed data structure
9
+ # (put, add etc) <b>after reading and then decrypting it</b> from a
10
+ # file and <b>before encrypting and then writing it</b> to a file.
11
+ #
12
+ # It provides behaviour to which we can create, append (add), update
13
+ # (change), read parts and delete essentially two structures
14
+ #
15
+ # - a collection of name/value pairs
16
+ # - an ordered list of values
17
+ #
18
+ # == JSON is Not Exposed in the Interface
19
+ #
20
+ # A key/value database doesn't expose the data format used in the implementation
21
+ # allowing this to be changed seamlessly to YAMl or other formats.
22
+ #
23
+ # == Symmetric Encryption and Decryption
24
+ #
25
+ # A key/value database supports operations to <b>read from</b> and <b>write to</b>
26
+ # a known filepath and with a symmetric key it can
27
+ #
28
+ # - decrypt <b>after reading from</b> a file and
29
+ # - encrypt <b>before writing to</b> a (the same) file
30
+ #
31
+ # == Hashes as the Primary Data Structure
32
+ #
33
+ # The key/value database openly extends {Hash} as the data structure for holding
34
+ #
35
+ # - strings
36
+ # - arrays
37
+ # - other hashes
38
+ # - booleans
39
+ # - integers and floats
40
+ class KeyDb < Hash
41
+
42
+ # Return a key database data structure that is instantiated from
43
+ # the parameter JSON string.
44
+ #
45
+ # @param db_json_string [String]
46
+ # this json formatted data structure will be converted into a
47
+ # a Ruby hash (map) data structure and returned.
48
+ #
49
+ # @return [KeyDb]
50
+ # a hash data structure that has been instantiated as per the
51
+ # parameter json string content.
52
+ def self.from_json( db_json_string )
53
+
54
+ data_db = KeyDb.new()
55
+ data_db.merge!( JSON.parse( db_json_string ) )
56
+ return data_db
57
+
58
+ end
59
+
60
+
61
+
62
+ # Create a new key value entry inside a dictionary with the specified
63
+ # name at the root of this database. Successful completion means the
64
+ # named dictionary will contain one more entry than it need even if it
65
+ # did not previously exist.
66
+ #
67
+ # @param dictionary_name [String]
68
+ #
69
+ # if a dictionary with this name exists at the root of the
70
+ # database add the parameter key value pair into it.
71
+ #
72
+ # if no dictionary exists then create one first before adding
73
+ # the key value pair as the first entry into it.
74
+ #
75
+ # @param key_name [String]
76
+ #
77
+ # the key part of the key value pair that will be added into the
78
+ # dictionary whose name was provided in the first parameter.
79
+ #
80
+ # @param value [String]
81
+ #
82
+ # the value part of the key value pair that will be added into the
83
+ # dictionary whose name was provided in the first parameter.
84
+ def create_entry( dictionary_name, key_name, value )
85
+
86
+ KeyError.not_new( dictionary_name, self )
87
+ KeyError.not_new( key_name, self )
88
+ KeyError.not_new( value, self )
89
+
90
+ self[ dictionary_name ] = {} unless self.has_key?( dictionary_name )
91
+ self[ dictionary_name ][ key_name ] = value
92
+
93
+ end
94
+
95
+
96
+ # Create a new secondary tier map key value entry inside a primary tier
97
+ # map at the map_key_name location.
98
+ #
99
+ # A failure will occur if either the outer or inner keys already exist
100
+ # without their values being map objects.
101
+ #
102
+ # If this method is called against a new empty map, the resulting map
103
+ # structure will look like the below.
104
+ #
105
+ # { outer_keyname ~> { inner_keyname ~> { entry_keyname, entry_value } } }
106
+ #
107
+ # @param outer_keyname [String]
108
+ #
109
+ # if a dictionary with this name exists at the root of the
110
+ # database add the parameter key value pair into it.
111
+ #
112
+ # if no dictionary exists then create one first before adding
113
+ # the key value pair as the first entry into it.
114
+ #
115
+ # @param inner_keyname [String]
116
+ #
117
+ # if a map exists at this key name then an entry comprising of
118
+ # a map_entry_key and a entry_value may either be added
119
+ # (if the map_entry_key does not already exist), or updated if
120
+ # it does.
121
+ #
122
+ # if the map does not exist it will be created and its first and
123
+ # only entry will be a key with inner_keyname along with a new
124
+ # single entry map consisting of the entry_keyname and the
125
+ # entry_value.
126
+ #
127
+ # @param entry_keyname [String]
128
+ #
129
+ # this key will exist in the second tier map after this operation.
130
+ #
131
+ # @param entry_value [String]
132
+ #
133
+ # this value will exist in the second tier map after this operation
134
+ # and if the entry_keyname already existed its value is overwritten
135
+ # with this one.
136
+ #
137
+ def create_map_entry( outer_keyname, inner_keyname, entry_keyname, entry_value )
138
+
139
+ KeyError.not_new( outer_keyname, self )
140
+ KeyError.not_new( inner_keyname, self )
141
+ KeyError.not_new( entry_keyname, self )
142
+ KeyError.not_new( entry_value, self )
143
+
144
+ self[ outer_keyname ] = {} unless self.has_key?( outer_keyname )
145
+ self[ outer_keyname ][ inner_keyname ] = {} unless self[ outer_keyname ].has_key?( inner_keyname )
146
+ self[ outer_keyname ][ inner_keyname ][ entry_keyname ] = entry_value
147
+
148
+ end
149
+
150
+
151
+ # Does this database have an entry in the root dictionary named with
152
+ # the key_name parameter?
153
+ #
154
+ # @param dictionary_name [String]
155
+ #
156
+ # immediately return false if a dictionary with this name does
157
+ # <b>not exist</b> at the root of this database.
158
+ #
159
+ # @param key_name [String]
160
+ #
161
+ # test whether a key/value pair answering to this name exists inside
162
+ # the specified dictionary at the root of this database.
163
+ #
164
+ def has_entry?( dictionary_name, key_name )
165
+
166
+ KeyError.not_new( dictionary_name, self )
167
+ KeyError.not_new( key_name, self )
168
+
169
+ return false unless self.has_key?( dictionary_name )
170
+ return self[ dictionary_name ].has_key?( key_name )
171
+
172
+ end
173
+
174
+
175
+ # Get the entry with the key name in a dictionary that is itself
176
+ # inside another dictionary (named in the first parameter) which
177
+ # thankfully is at the root of this database.
178
+ #
179
+ # Only call this method if {has_entry?} returns true for the same
180
+ # dictionary and key name parameters.
181
+ #
182
+ # @param dictionary_name [String]
183
+ #
184
+ # get the entry inside a dictionary which is itself inside a
185
+ # dictionary (with this dictionary name) which is itself at the
186
+ # root of this database.
187
+ #
188
+ # @param key_name [String]
189
+ #
190
+ # get the value part of the key value pair that is inside a
191
+ # dictionary (with the above dictionary name) which is itself
192
+ # at the root of this database.
193
+ #
194
+ def get_entry( dictionary_name, key_name )
195
+
196
+ return self[ dictionary_name ][ key_name ]
197
+
198
+ end
199
+
200
+
201
+ # Delete an existing key value entry inside the dictionary with the specified
202
+ # name at the root of this database. Successful completion means the
203
+ # named dictionary will contain one less entry if that key existed.
204
+ #
205
+ # @param dictionary_name [String]
206
+ #
207
+ # if a dictionary with this name exists at the root of the
208
+ # database add the parameter key value pair into it.
209
+ #
210
+ # if no dictionary exists throw an error
211
+ #
212
+ # @param key_name [String]
213
+ #
214
+ # the key part of the key value pair that will be deleted in the
215
+ # dictionary whose name was provided in the first parameter.
216
+ def delete_entry( dictionary_name, key_name )
217
+
218
+ KeyError.not_new( dictionary_name, self )
219
+ KeyError.not_new( key_name, self )
220
+
221
+ self[ dictionary_name ].delete( key_name )
222
+
223
+ end
224
+
225
+
226
+ # Read and inject into this envelope, the data structure found in a
227
+ # file at the path specified in the first parameter.
228
+ #
229
+ # Symmetric cryptography is mandatory for the envelope so we must
230
+ # <b>encrypt before writing</b> and <b>decrypt after reading</b>.
231
+ #
232
+ # An argument error will result if a suitable key is not provided.
233
+ #
234
+ # If the file does not exist (denoting the first read) all this method
235
+ # does is to stash the filepath as an instance variable and igore the
236
+ # decryption key which can be nil (or ommitted).
237
+ #
238
+ # @param the_filepath [String]
239
+ # absolute path to the file which acts as the persistent mirror to
240
+ # this data structure envelope.
241
+ #
242
+ # @param decryption_key [String]
243
+ # encryption at rest is a given so this mandatory parameter must
244
+ # contain a robust symmetric decryption key. The key will be used
245
+ # for decryption after the read and it will not linger (ie not cached
246
+ # as an instance variable).
247
+ #
248
+ # @raise [ArgumentError] if the decryption key is not robust enough.
249
+ def read the_filepath, decryption_key = nil
250
+
251
+ # @todo -> this is confused - it uses INI but above methods use JSON
252
+ # @todo -> this is confused - it uses INI but above methods use JSON
253
+ # @todo -> this is confused - it uses INI but above methods use JSON
254
+ # @todo -> this is confused - it uses INI but above methods use JSON
255
+ # @todo -> this is confused - it uses INI but above methods use JSON
256
+ # @todo -> this is confused - it uses INI but above methods use JSON
257
+ # @todo -> this is confused - it uses INI but above methods use JSON
258
+ # @todo -> this is confused - it uses INI but above methods use JSON
259
+ # @todo -> this is confused - it uses INI but above methods use JSON
260
+ # @todo -> this is confused - it uses INI but above methods use JSON
261
+
262
+ raise RuntimeError, "This KeyDb.read() software is never called so how can I be here?"
263
+
264
+ @filepath = the_filepath
265
+ return unless File.exists? @filepath
266
+
267
+ cipher_text = Base64.decode64( File.read( @filepath ).strip )
268
+ plain_text = ToolBelt::Blowfish.decryptor( cipher_text, decryption_key )
269
+
270
+ data_structure = JSON.parse plain_text
271
+ self.merge! data_structure
272
+
273
+ end
274
+
275
+
276
+ # Write the data in this envelope hash map into a file-system
277
+ # backed mirror whose path was specified in the {self.read} method.
278
+ #
279
+ # Technology for encryption at rest is supported by this dictionary
280
+ # and to this aim, please endeavour to post a robust symmetric
281
+ # encryption key.
282
+ #
283
+ # Calling this {self.write} method when the file at the prescribed path
284
+ # does not exist results in the directory structure being created
285
+ # (if necessary) and then the encrypted file being written.
286
+ #
287
+ # @param encryption_key [String]
288
+ # encryption at rest is a given so this mandatory parameter must
289
+ # contain a robust symmetric encryption key. The symmetric key will
290
+ # be used for the decryption after the read. Note that the decryption
291
+ # key does not linger meaning it isn't cached in an instance variable.
292
+ def write encryption_key
293
+
294
+ # @todo -> this is confused - it uses INI but above methods use JSON
295
+ # @todo -> this is confused - it uses INI but above methods use JSON
296
+ # @todo -> this is confused - it uses INI but above methods use JSON
297
+ # @todo -> this is confused - it uses INI but above methods use JSON
298
+ # @todo -> this is confused - it uses INI but above methods use JSON
299
+ # @todo -> this is confused - it uses INI but above methods use JSON
300
+ # @todo -> this is confused - it uses INI but above methods use JSON
301
+ # @todo -> this is confused - it uses INI but above methods use JSON
302
+ # @todo -> this is confused - it uses INI but above methods use JSON
303
+
304
+ raise RuntimeError, "This KeyDb.write( key ) software is never called so how can I be here?"
305
+
306
+ FileUtils.mkdir_p(File.dirname(@filepath))
307
+ cipher_text = Base64.encode64 ToolBelt::Blowfish.encryptor( self.to_json, encryption_key )
308
+ File.write @filepath, cipher_text
309
+
310
+ puts ""
311
+ puts "=== ============================"
312
+ puts "=== Envelope State ============="
313
+ puts "=== ============================"
314
+
315
+ a_ini_file = IniFile.new
316
+ self.each_key do |section_name|
317
+ a_ini_file[section_name] = self[section_name]
318
+ end
319
+ puts a_ini_file.to_s
320
+
321
+ puts "=== ============================"
322
+ puts ""
323
+
324
+ end
325
+
326
+
327
+ end
328
+
329
+
330
+ end
@@ -0,0 +1,195 @@
1
+ #!/usr/bin/ruby
2
+
3
+ # The open key library generates keys, it stores their salts, it produces differing
4
+ # representations of the keys (like base64 for storage and binary for encrypting).
5
+ #
6
+ # == Key Class Names their and Responsibility
7
+ #
8
+ # The 5 core key classes in the open key library are
9
+ #
10
+ # - {Key} represents keys in bits, binary and base 64 formats
11
+ # - {Key64} for converting from to and between base 64 characters
12
+ # - {Key256} uses key derivation functions to produce high entropy keys
13
+ # - {KeyIO} reads and writes key metadata (like salts) from/to persistent storage
14
+ # - {KeyCycle} for creating and locking the keys that underpin the security
15
+ #
16
+ # == The 5 Core Key Classes
17
+ #
18
+ # Key To initialize with a 264 binary bit string. To hold the
19
+ # key and represent it when requested
20
+ # - as a 264 bit binary bit string
21
+ # - as a 256 bit binary bit string
22
+ # - as a 256 bit raw bytes encryption key
23
+ # - as a YACHT64 formatted string
24
+ #
25
+ # Key64 To map in and out of the Yacht64 character set - from and to
26
+ # - a binary bit string sequence
27
+ # - a Base64 character encoding
28
+ # - a UrlSafe Base64 character encoding
29
+ # - a Radix64 character encoding
30
+ #
31
+ # Key256 It generates a key in 3 different and important ways. It can
32
+ # generate
33
+ #
34
+ # (a) from_password
35
+ # (b) from_random (or it can)
36
+ # (c) regenerate
37
+ #
38
+ # When generating from a password it takes a dictionary with
39
+ # a pre-tailored "section" and writes BCrypt and Pbkdf2 salts
40
+ # into it.
41
+ #
42
+ # When generating random it kicks of by creating a 55 byte
43
+ # random key fo BCrypt and a 64 byte random key for Pbkdf2.
44
+ # It then calls upon generate_from_password.
45
+ #
46
+ # When regenerating it queries the dictionary provided at the
47
+ # pre-tailored "section" for the BCrypt and Pbkdf2 salts and
48
+ # then uses input passwords (be they human randomly sourced)
49
+ # and regenerates the keys it produced at an earlier sitting.
50
+ #
51
+ # KeyIO KeyIO is instantiated with a folder path and a "key reference".
52
+ # KeyIO will then manage writing to and rereading from the structure
53
+ # hel inside th efile. The file is named (primarily) by the
54
+ # reference string.
55
+ #
56
+ # KeyCycle KeyLifeCycle implements the merry go round that palms off
57
+ # responsibility to the intra-session cycle and then back again
58
+ # to ever rotary inter-session(ary) cycle.
59
+ ########### Maybe think of a method where we pass in
60
+ ########### 2 secrets - 1 human and 1 55 random bytes (session)
61
+ ###########
62
+ ########### 1 another 55 random key is created (the actual encryption key)
63
+ ########### 2 then the above key is encrypted TWICE (2 diff salts and keys)
64
+ ########### 3 Once by key from human password
65
+ ########### 4 Once by key from machine password
66
+ ########### 5 then the key from 1 is returned
67
+ ########### 6 caller encrypts file .................... (go 4 it)
68
+
69
+
70
+ # Generates a 256 bit symmetric encryption key derived from a random
71
+ # seed sequence of 55 bytes. These 55 bytes are then fed into the
72
+ # {from_password} key derivation function and processed in a similar
73
+ # way to if a human had generated the string.
74
+ #
75
+
76
+
77
+ # <b>Key derivation functions</b> exist to convert <b>low entropy</b> human
78
+ # created passwords into a high entropy key that is computationally difficult
79
+ # to acquire through brute force.
80
+ #
81
+ # == SafeDb's Underlying Security Strategy
82
+ #
83
+ # <b>Randomly generate a 256 bit encryption key and encrypt it</b> with a key
84
+ # derived from a human password and generated by at least two cryptographic
85
+ # workhorses known as <b>key derivation functions</b>.
86
+ #
87
+ # The encryption key (encrypted by the one derived from a human password) sits
88
+ # at the beginning of a long chain of keys and encryption - so much so that the
89
+ # crypt material being outputted for storage is all but worthless to anyone but
90
+ # its rightful owner.
91
+ #
92
+ # == Key Size vs Crack Time
93
+ #
94
+ # Cracking a 256 bit key would need roughly 2^255 iterations (half the space)
95
+ # and this is akin to the number of atoms in the known universe.
96
+ #
97
+ # <b>The human key can put security at risk.</b>
98
+ #
99
+ # The rule of thumb is that a 40 character password with a good spread of the
100
+ # roughly 90 typable characters, would produce security equivalent to that of
101
+ # an AES 256bit key. As the password size and entropy drop, so does the security,
102
+ # exponentially.
103
+ #
104
+ # As human generated passwords have a relatively small key space, key derivation
105
+ # functions must be slow to compute with any implementation.
106
+ #
107
+ # == Key Derivation Functions for Command Line Apps
108
+ #
109
+ # A command line app (with no recourse to a central server) uses a Key
110
+ # Derivation Function (like BCrypt, Aaron2 or PBKD2) in a manner different
111
+ # to that employed by server side software.
112
+ #
113
+ # - server side passwords are hashed then both salt and hash are persisted
114
+ # - command line apps do not store the key - they only store the salt
115
+ # - both throw away the original password
116
+ #
117
+ # == One Key | One Session | One Crypt
118
+ #
119
+ # Command line apps use the derived key to <b>symmetrically encrypt and decrypt</b>
120
+ # one and only one 48 character key and a new key is derived at the beginning
121
+ # of every session.
122
+ #
123
+ # At the end of the session <b>all material encrypted by the outgoing key</b>
124
+ # is removed. This aggressive key rotation strategy leaves no stone unturned in
125
+ # the quest for ultimate security.
126
+ #
127
+ # == SafeDb's CLI Key Derivation Architecture
128
+ #
129
+ # SafeDb never accesses another server and giving its users total control
130
+ # of their secret crypted materials. It strengthens the key derivation process
131
+ # in three important ways.
132
+ #
133
+ # - [1] it does not store the key nor does it store the password
134
+ #
135
+ # - [2] a new master key is generated for every session only to hold the master index file
136
+ #
137
+ # - [3] it uses both <b>BCrypt</b> (Blowfish Crypt) and the indefatigable <b>PBKD2</b>
138
+
139
+
140
+ # After a successful initialization, the application instance is linked to a keystore
141
+ # whose contents are responsible for securing the application instance database.
142
+ #
143
+ # To ascertain what needs to be done to bridge the gap to full initialization the
144
+ # app needs to know 3 things from the KeyApi. These things are
145
+ #
146
+ # - the ID of this app instance on the machine
147
+ # - if a keystore been associated with this ID
148
+ # - whether the keystore secures the app database
149
+ #
150
+ # The answers dictate the steps that need to be undertaken to bring the database of
151
+ # the application instance under the secure wing of the KeyApi.
152
+ #
153
+ #
154
+ # == 1. What is the App Instance ID on this Machine?
155
+ #
156
+ # The KeyApi uses the "just given" application reference and the machine environment to
157
+ # respond with a <b>digested identifier</b> binding the application instance to the
158
+ # present machine (workstation).
159
+ #
160
+ #
161
+ # == 2. Has a Keystore been associated with this ID?
162
+ #
163
+ # The application's configuration manager is asked to find an associated KeyStore ID
164
+ # mapped against the app/machine id garnered by question 1.
165
+ #
166
+ # <b>No it has not!</b>
167
+ #
168
+ # If <b>NO</b> then a KeyStore ID is acquired <b>either from the init command's parameter</b>,
169
+ # or a <b>suitable default</b>. This new association between the app/machine ID and the
170
+ # KeyStore ID is then stored so the answer next time will be <b>YES</b>.
171
+ #
172
+ # <b>Yes it has!</b>
173
+ #
174
+ # Great - we now submit the KeyStore ID to the KeyApi so that it may answer question 3.
175
+ #
176
+ #
177
+ # == 3. Does the keystore secure the app instance database?
178
+ #
179
+ # For the KeyApi to answer, it needs the App's Instance ID and the KeyStore ID.
180
+ #
181
+ # <b>Not Yet!</b> Now <b>NO</b> means this application instance's database has not been
182
+ # brought under the protection of the KeyApi's multi-layered security net. For this it
183
+ # needs
184
+ #
185
+ # - the KeyStore ID
186
+ # - the application instance reference
187
+ # - the plaintext secret from which nothing of the host survives
188
+ # - the current application database plaintext
189
+ #
190
+ # <b>Yes it does!</b> If the app db keys <b>have been instantiated</b> and the client app is
191
+ # <b>sitting pretty</b> in possession of the database ciphertext, no more needs doing.
192
+
193
+ module SafeDb
194
+
195
+ end
@@ -0,0 +1,110 @@
1
+ #!/usr/bin/ruby
2
+
3
+ module SafeDb
4
+
5
+
6
+ # This class is the parent to all opensession errors
7
+ # that originate from the command line.
8
+ #
9
+ # All opensession cli originating errors are about
10
+ #
11
+ # - a problem with the input or
12
+ # - a problem with the current state or
13
+ # - a predictable future problem
14
+ class KeyError < StandardError
15
+
16
+
17
+ # Initialize the error and provide a culprit
18
+ # object which will be to-stringed and given
19
+ # out as evidence (look at this)!
20
+ #
21
+ # This method will take care of loggin the error.
22
+ #
23
+ # @param message [String] human readable error message
24
+ # @param culprit [Object] object that is either pertinent, a culprit or culpable
25
+ def initialize message, culprit
26
+
27
+ super(message)
28
+
29
+ @the_culprit = culprit
30
+
31
+ log.info(x) { "An [Error] Occured => #{message}" }
32
+ log.info(x) { "Object of Interest => #{culprit.to_s}" } unless culprit.nil?
33
+ log.info(x) { "Class Name Culprit => #{culprit.class.name}" }
34
+ log.info(x) { "Error Message From => #{self.class.name}" }
35
+
36
+ thread_backtrace = Thread.current.backtrace.join("\n")
37
+ thread_backtrace.to_s.log_lines
38
+
39
+ end
40
+
41
+
42
+ # This method gives interested parties the object that
43
+ # is at the centre of the exception. This object is either
44
+ # very pertinent, culpable or at the very least, interesting.
45
+ #
46
+ # @return [String] string representation of culpable object
47
+ def culprit
48
+ return "No culprit identified." if @the_culprit.nil?
49
+ return @the_culprit.to_s
50
+ end
51
+
52
+
53
+ # Assert that the parameter string attribute is <b>not new</b> which
54
+ # means neither nil, nor empty nor consists solely of whitespace.
55
+ #
56
+ # The <b>NEW</b> acronym tells us that a bearer worthy of the name is
57
+ #
58
+ # - neither <b>N</b>il
59
+ # - nor <b>E</b>mpty
60
+ # - nor consists solely of <b>W</b>hitespace
61
+ #
62
+ # @param the_attribute [String]
63
+ # raise a {KeyError} if the attribute is not new.
64
+ #
65
+ # @param the_desc [String]
66
+ # a description of th attribute
67
+ #
68
+ # @raise [KeyError]
69
+ #
70
+ # The attribute cannot be <b>NEW</b>. The <b>NEW acronym</b> asserts
71
+ # that the attribute is
72
+ #
73
+ # - neither <b>N</b>il
74
+ # - nor <b>E</b>mpty
75
+ # - nor <b>W</b>hitespace only
76
+ #
77
+ def self.not_new the_attribute, the_desc
78
+
79
+ attribute_new = the_attribute.nil? || the_attribute.chomp.strip.empty?
80
+ return unless attribute_new
81
+
82
+ msg = "[the_desc] is either nil, empty or consists solely of whitespace."
83
+ raise KeyError.new( msg, the_desc )
84
+
85
+ end
86
+
87
+
88
+ end
89
+
90
+ =begin
91
+ # Throw this error if the configured safe directory points to a file.
92
+ class SafeDirectoryIsFile < OpenError::CliError; end;
93
+
94
+ # Throw this error if safe directory path is either nil or empty.
95
+ class SafeDirNotConfigured < OpenError::CliError; end;
96
+
97
+ # Throw this error if the email address is nil, empty or less than 5 characters.
98
+ class EmailAddrNotConfigured < OpenError::CliError; end;
99
+
100
+ # Throw this error if the store url is either nil or empty.
101
+ class StoreUrlNotConfigured < OpenError::CliError; end;
102
+
103
+ # Throw if "prime folder" name occurs 2 or more times in the path.
104
+ class SafePrimeNameRepeated < OpenError::CliError; end;
105
+
106
+ # Throw if "prime folder" name occurs 2 or more times in the path.
107
+ class SafePrimeNameNotAtEnd < OpenError::CliError; end;
108
+ =end
109
+
110
+ end