safedb 0.01.0001

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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,585 @@
1
+ #!/usr/bin/ruby
2
+
3
+ module SafeDb
4
+
5
+ # First use the class methods to source keys, then use a key's instance
6
+ # methods to access its properties and in concert with other symmetrical
7
+ # information, you can use the keys to lock (encrypt) or unlock (decrypt)
8
+ # other keys and objecs.
9
+ #
10
+ # == Sourcing and Deriving Keys
11
+ #
12
+ # Keys can be
13
+ #
14
+ # - sourced from a secure random byte generating function
15
+ # - sourced from ciphertext and another (decryption) key
16
+ # - generated by passing a secret through key derivation functions
17
+ # - regenerated from a secret and previously stored salts
18
+ # - sourced from the current unique workstation shell environment
19
+ # - sourced from an environment variable containing ciphertext
20
+ #
21
+ #
22
+ # Keys need to be viewed (represented) in multiple ways and the essence
23
+ # of the key viewer is to input keys {as_bits}, {as_bytes} and {as_base64}
24
+ # and then output the same key (in as far as is possible) - as bits, as
25
+ # bytes and as base64.
26
+ #
27
+ # == Key | To and From Behaviour
28
+ #
29
+ # Use the <b>From</b> methods to create Keys from a variety of resources
30
+ # such as
31
+ #
32
+ # - a base64 encoded string
33
+ # - a binary byte string
34
+ # - a string of one and zero bits
35
+ # - a hexadecimal representation
36
+ #
37
+ # Once you have instantiated the key, you will then be able to convert it
38
+ # (within reason due to bit, byte and base64 lengths) to any of the above
39
+ # key representations.
40
+ #
41
+ # == Key | Bits Bytes and Base64
42
+ #
43
+ # The shoe doesn't always fit when its on the other foot and this is best
44
+ # illustratd with a table that maps bits to 8 bit bytes and 6 bit Base64
45
+ # characters.
46
+ #
47
+ # | --------- | -------- | ------------ | ------------------------------- |
48
+ # | Fit? | Bits | Bytes | (and) Base64 |
49
+ # | --------- | -------- | ------------ | ------------------------------- |
50
+ # | Perfect | 168 Bits | is 21 bytes | 28 Chars - bcrypt chops to this |
51
+ # | Perfect | 216 Bits | is 27 bytes | 36 Chars - |
52
+ # | Perfect | 264 Bits | is 33 bytes | 44 Chars - holder 4 256bit keys |
53
+ # | Perfect | 384 Bits | is 48 bytes | 64 Chars - 216 + 168 equals 384 |
54
+ # | --------- | -------- | ------------ | ------------------------------- |
55
+ # | Imperfect | 128 Bits | 16 precisely | 22 Chars - 21 + 2 remain bits |
56
+ # | Imperfect | 186 Bits | 23 remain 2 | 31 Characers precisely |
57
+ # | Imperfect | 256 Bits | 32 precisely | 43 Chars - 42 + 4 remain bits |
58
+ # | --------- | -------- | ------------ | ------------------------------- |
59
+ #
60
+ # Yes, the shoe doesn't always fit when it's on the other foot.
61
+ #
62
+ # == Schoolboy Error
63
+ #
64
+ # <b>The strategy is so simple, we call it a schoolboy error.</b>
65
+ #
66
+ # If we want to use a key with n bits and either n % 6 or n % 8 (or both)
67
+ # are not zero - <b>we instantiate a Key</b> with the lowest common
68
+ # denominator of 6 and 8 that exceeds n.
69
+ #
70
+ # So when we request a byte, or base64 representation the viewer will
71
+ # truncate (not round down) to the desired length.
72
+ #
73
+ # == Mapping Each Character to 6 Binary Bits
74
+ #
75
+ # We need 6 binary bits to represent a base64 character (and 4
76
+ # bits for hexadecimal). Here is an example mapping between
77
+ # a base 64 character, an integer and the six bit binary.
78
+ #
79
+ # Character Integer Binary (6 Bit)
80
+ #
81
+ # a 0 000000
82
+ # b 1 000001
83
+ # c 2 000010
84
+ #
85
+ # y 25 011001
86
+ # z 26 011010
87
+ # A 27 011011
88
+ # B 28 011100
89
+ #
90
+ # 8 60 111100
91
+ # 9 61 111101
92
+ # / 62 111110
93
+ # + 63 111111
94
+ #
95
+ class Key
96
+
97
+ # Initialize a key object from a bit string of ones and zeroes provided
98
+ # in the parameter string.
99
+ #
100
+ # For example a string of 384 bits (ones and zeroes) can be thought of
101
+ # as a 48 byte key which can also be represented with 64 more compact
102
+ # base64 characters.
103
+ #
104
+ # | -------- | ------------ | -------------------------------- |
105
+ # | Bits | Bytes | Base64 |
106
+ # | -------- | ------------ | -------------------------------- |
107
+ # | 384 Bits | is 48 bytes | and 64 characters |
108
+ # | -------- | ------------ | -------------------------------- |
109
+ #
110
+ # @param the_bit_string [String]
111
+ # the bit string of ones and zeroes that represents the bits that
112
+ # represent this key
113
+ def initialize the_bit_string
114
+ @bit_string = the_bit_string
115
+ end
116
+
117
+
118
+ # Return a (secure) randomly generated super high entropy 384 bit key
119
+ # that can be stored with <b>64 base64 characters</b> and used to
120
+ # <b><em>source digest functions</em></b> that can unreversibly convert
121
+ # the key to a <b>256 bit symmetric encryption key</b>.
122
+ #
123
+ # | -------- | ------------ | -------------------------------- |
124
+ # | Bits | Bytes | Base64 |
125
+ # | -------- | ------------ | -------------------------------- |
126
+ # | 384 Bits | is 48 bytes | and 64 characters |
127
+ # | -------- | ------------ | -------------------------------- |
128
+ #
129
+ # This key easily translates to a base64 and/or byte array format because
130
+ # the 384 bit count is a <b>multiple of both 6 and 8</b>.
131
+ #
132
+ # @return [SafeDb::Key]
133
+ # return a key containing 384 random bits (or a random array of 48 bytes)
134
+ # which can if necessary be serialized into 64 base64 characters.
135
+ #
136
+ # @raise [ArgumentError]
137
+ # If a nil or zero length byte array is received.
138
+ # Or if the number of bytes <b>multiplied by 8</b>
139
+ # is <b>not a multiple of 6</b>.
140
+ def self.from_random
141
+ return Key.new( to_random_bits( RANDOM_KEY_BYTE_LENGTH ) )
142
+ end
143
+
144
+
145
+ def self.to_random_bits the_byte_length
146
+ random_bit_string = ""
147
+ for n in 1 .. the_byte_length
148
+ random_integer = SecureRandom.random_number( EIGHT_BIT_INTEGER_SIZE )
149
+ random_bit_string += "%08d" % [ random_integer.to_s(2) ]
150
+ end
151
+ return random_bit_string
152
+ end
153
+
154
+
155
+ # Return the key represented by the parameter sequence of base64
156
+ # characters.
157
+ #
158
+ # @param char64_string [String]
159
+ #
160
+ # The base64 character sequence which the returned key is
161
+ # instantiated from. Naturally this character sequencee cannot
162
+ # be nil, nor can it contain any characters that are not
163
+ # present in {Key64::YACHT64_CHARACTER_SET}.
164
+ #
165
+ # Ideally the number of parameter characters multiplied by 6
166
+ # <b>should be a multiple of eight (8)</b> otherwise the new
167
+ # key's bit string will require padding and extension.
168
+ #
169
+ # @return [SafeDb::Key]
170
+ # return a key from the parameter sequence of base64 characters.
171
+ #
172
+ # @raise [ArgumentError]
173
+ # If a nil or zero length byte array is received.
174
+ # Or if the number of bytes <b>multiplied by 8</b>
175
+ # is <b>not a multiple of 6</b>.
176
+ def self.from_char64 char64_string
177
+ return Key.new( Key64.to_bits( char64_string ) )
178
+ end
179
+
180
+
181
+ # Return a key represented by the parameter binary string.
182
+ #
183
+ # @param binary_text [String]
184
+ # The binary string that the returned key will be
185
+ # instantiated from.
186
+ #
187
+ # @return [SafeDb::Key]
188
+ # return a key from the binary byte string parameter
189
+ def self.from_binary binary_text
190
+ ones_and_zeroes = binary_text.unpack("B*")[0]
191
+ return Key.new( ones_and_zeroes )
192
+ end
193
+
194
+
195
+ # Convert a string of Radix64 characters into a key.
196
+ #
197
+ # This method converts the base64 string into the internal YACHT64 format
198
+ # and then converts that into a bit string so that a key can be instantiated.
199
+ #
200
+ # @param radix64_string [String]
201
+ # the radix64 string to convert into akey. This string will be a subset
202
+ # of the usual 62 character suspects together with period and forward
203
+ # slash characters.
204
+ #
205
+ # This parameter should not contain newlines nor carriage returns.
206
+ #
207
+ # @return [SafeDb::Key]
208
+ # return a key from the parameter sequence of base64 characters.
209
+ #
210
+ # @raise [ArgumentError]
211
+ # If a nil or zero length parameter array is received.
212
+ def self.from_radix64 radix64_string
213
+ return Key.new( Key64.from_radix64_to_bits( radix64_string ) )
214
+ end
215
+
216
+
217
+ # When a key is initialized, it is internally represented as a
218
+ # string of ones and zeroes primarily for simplicity and can be
219
+ # visualized as bits that are either off or on.
220
+ #
221
+ # Once internalized a key can also be represented as
222
+ #
223
+ # - a sequence of base64 (or radix64) characters (1 per 6 bits)
224
+ # - a binary string suitable for encryption (1 byte per 8 bits)
225
+ # - a 256bit encryption key from Digest(ing) the binary form
226
+ #
227
+ # @return [String]
228
+ # a string of literally ones and zeroes that represent the
229
+ # sequence of bits making up this key.
230
+ def to_s
231
+
232
+ ## Write duplicate ALIAS method called ==> to_bits() <== (bits and pieces)
233
+ ## Write duplicate ALIAS method called ==> to_bits() <== (bits and pieces)
234
+ ## Write duplicate ALIAS method called ==> to_bits() <== (bits and pieces)
235
+ ## Write duplicate ALIAS method called ==> to_bits() <== (bits and pieces)
236
+ ## Write duplicate ALIAS method called ==> to_bits() <== (bits and pieces)
237
+ ## Write duplicate ALIAS method called ==> to_bits() <== (bits and pieces)
238
+ ## Write duplicate ALIAS method called ==> to_bits() <== (bits and pieces)
239
+
240
+ ## ---------------------------------------------
241
+ ## +++++++++ WARNING ++++++++
242
+ ## ---------------------------------------------
243
+ ##
244
+ ## to_s does not need 2b called
245
+ ## So both the below print the same.
246
+ ##
247
+ ## So YOU MUST KEEP the to_s method until a proper test suite is in place.
248
+ ## So YOU MUST KEEP the to_s method until a proper test suite is in place.
249
+ ##
250
+ ## puts "#{the_key}"
251
+ ## puts "#{the_key.to_s}"
252
+ ##
253
+ ## So YOU MUST KEEP the to_s method until a proper test suite is in place.
254
+ ## So YOU MUST KEEP the to_s method until a proper test suite is in place.
255
+ ##
256
+ ## ---------------------------------------------
257
+
258
+ return @bit_string
259
+ end
260
+
261
+
262
+ # Convert this keys bit value into a printable character set
263
+ # that is suitable for storing in multiple places such as
264
+ # <b>environment variables</b> and <b>INI files</b>.
265
+ #
266
+ # @return [String]
267
+ # printable characters from a set of 62 alpha-numerics
268
+ # plus an @ symbol and a percent % sign.
269
+ #
270
+ # @raise ArgumentError
271
+ # If the bit value string for this key is nil.
272
+ # Or if the bit string length is not a multiple of six.
273
+ # Or if it contains any character that is not a 1 or 0.
274
+ def to_char64
275
+ assert_non_nil_bits
276
+ return Key64.from_bits( @bit_string )
277
+ end
278
+
279
+
280
+ # Return the <b>un-printable <em>binary</em> bytes</b> representation
281
+ # of this key. If you store 128 bits it will produce 22 characters
282
+ # because 128 divide by 6 is 21 characters and a remainder of two (2)
283
+ # bits.
284
+ #
285
+ # The re-conversion of the 22 characters will now produce 132 bits which
286
+ # is different from the original 128 bits.
287
+ #
288
+ # @return [Byte]
289
+ # a non-printable binary string of eight (8) bit bytes which can be
290
+ # used as input to both digest and symmetric cipher functions.
291
+ def to_binary
292
+ return [ to_s ].pack("B*")
293
+ end
294
+
295
+
296
+ # Return the <b>un-printable <em>binary</em> bytes</b> representation
297
+ # of this key. If you store 128 bits it will produce 22 characters
298
+ # because 128 divide by 6 is 21 characters and a remainder of two (2)
299
+ # bits.
300
+ #
301
+ # The re-conversion of the 22 characters will now produce 132 bits which
302
+ # is different from the original 128 bits.
303
+ #
304
+ # @return [Byte]
305
+ # a non-printable binary string of eight (8) bit bytes which can be
306
+ # used as input to both digest and symmetric cipher functions.
307
+ def self.to_binary_from_bit_string bit_string_to_convert
308
+ return [ bit_string_to_convert ].pack("B*")
309
+ end
310
+
311
+
312
+ # This method uses digests to convert the key's binary representation
313
+ # (which is either 48 bytes for purely random keys or 64 bytes for keys
314
+ # derived from human sourced secrets) into a key whose size is ideal for
315
+ # plying the ubiquitous <b>AES256 symmetric encryption algorithm</b>.
316
+ #
317
+ # This method should only ever be called when this key has been derived
318
+ # from either a (huge) <b>48 byte random source</b> or from a key derivation
319
+ # function (KDF) such as BCrypt, SCrypt, PBKDF2 or a union from which the
320
+ # 512 bit (64 byte) key can be reduced to 256 bits.
321
+ #
322
+ # @return [String]
323
+ # a binary string of thirty-two (32) eight (8) bit bytes which
324
+ # if appropriate can be used as a symmetric encryption key especially
325
+ # to the powerful AES256 cipher.
326
+ def to_aes_key
327
+ return Digest::SHA256.digest( to_binary() )
328
+ end
329
+
330
+
331
+ # This method uses the SHA384 digest to convert this key's binary
332
+ # representation into another (newly instantiated) key whose size
333
+ # is <b>precisely 384 bits</b>.
334
+ #
335
+ # If you take the returned key and call
336
+ #
337
+ # - {to_char64} you get a 64 character base64 string
338
+ # - {to_s} you get a string of 384 ones and zeroes
339
+ # - {to_binary} you get a 48 byte binary string
340
+ #
341
+ # @return [SafeDb::Key]
342
+ # a key with a bit length (ones and zeroes) of <b>precisely 384</b>.
343
+ def to_384_bit_key
344
+
345
+ a_384_bit_key = Key.from_binary( Digest::SHA384.digest( to_binary() ) )
346
+
347
+ has_384_chars = a_384_bit_key.to_s.length == 384
348
+ err_msg = "Digested key length is #{a_384_bit_key.to_s.length} instead of 384."
349
+ raise RuntimeError, err_msg unless has_384_chars
350
+
351
+ return a_384_bit_key
352
+
353
+ end
354
+
355
+
356
+ # Use the {OpenSSL::Cipher::AES256} block cipher in CBC mode and the binary
357
+ # 256bit representation of this key to encrypt the parameter key.
358
+ #
359
+ # Store the ciphertext provided by this method. To re-acquire (reconstitute)
360
+ # the parameter key use the {do_decrypt_key} decryption method with
361
+ # the ciphertext produced here.
362
+ #
363
+ # <b>Only Encrypt Strong Keys</b>
364
+ #
365
+ # Never encrypt a potentially weak key, like one derived from a human password
366
+ # (even though it is put through key derivation functions).
367
+ #
368
+ # Once generated (or regenerated) a potentially weak key should live only as
369
+ # long as it takes for it to encrypt a strong key. The strong key can then
370
+ # be used to encrypt valuable assets.
371
+ #
372
+ # <b>Enforcing Strong Key Size</b>
373
+ #
374
+ # If one key is potentially weaker than the other, the weaker key must be this
375
+ # object and the strong key is the parameter key.
376
+ #
377
+ # This method thus enforces the size of the strong key. A strong key has
378
+ # 384 bits of entropy, and is represented by 64 base64 characters.
379
+ #
380
+ # @param key_to_encrypt [SafeDb::Key]
381
+ # this is the key that will first be serialized into base64 and then locked
382
+ # down using the 256 bit binary string from this host object as the symmetric
383
+ # encryption key.
384
+ #
385
+ # This method is sensitive to the size of the parameter key and expects to
386
+ # encrypt <b>exactly 64 base64 characters</b> within the parameter key.
387
+ #
388
+ # @return [String]
389
+ # The returned ciphertext should be stored. Its breakdown is as follows.
390
+ # 96 bytes are returned which equates to 128 base64 characters.
391
+ # The random initialization vector (iv) accounts for the first 16 bytes.
392
+ # The actual crypt ciphertext then accounts for the final 80 bytes.
393
+ #
394
+ # @raise [ArgumentError]
395
+ # the size of the parameter (strong) key is enforced to ensure that it has
396
+ # exactly 384 bits of entropy which are represented by 64 base64 characters.
397
+ def do_encrypt_key key_to_encrypt
398
+
399
+ crypt_cipher = OpenSSL::Cipher::AES256.new(:CBC)
400
+
401
+ crypt_cipher.encrypt()
402
+ random_iv = crypt_cipher.random_iv()
403
+ crypt_cipher.key = to_aes_key()
404
+
405
+ calling_module = File.basename caller_locations(1,1).first.absolute_path, ".rb"
406
+ calling_method = caller_locations(1,1).first.base_label
407
+ calling_lineno = caller_locations(1,1).first.lineno
408
+ caller_details = "#{calling_module} | #{calling_method} | (line #{calling_lineno})"
409
+
410
+ cipher_text = crypt_cipher.update( key_to_encrypt.to_char64 ) + crypt_cipher.final
411
+
412
+ binary_text = random_iv + cipher_text
413
+ ones_zeroes = binary_text.unpack("B*")[0]
414
+ ciphertxt64 = Key64.from_bits( ones_zeroes )
415
+
416
+ size_msg = "Expected bit count is #{EXPECTED_CIPHER_BIT_LENGTH} not #{ones_zeroes.length}."
417
+ raise RuntimeError, size_msg unless ones_zeroes.length == EXPECTED_CIPHER_BIT_LENGTH
418
+
419
+ return ciphertxt64
420
+
421
+ end
422
+
423
+
424
+ # Use the {OpenSSL::Cipher::AES256} block cipher in CBC mode and the binary
425
+ # 256bit representation of this key to decrypt the parameter ciphertext and
426
+ # return the previously encrypted key.
427
+ #
428
+ # To re-acquire (reconstitute) the original key call this method with the
429
+ # stored ciphertext that was returned by the {do_encrypt_key}.
430
+ #
431
+ # <b>Only Encrypt Strong Keys</b>
432
+ #
433
+ # Never encrypt a potentially weak key, like one derived from a human password
434
+ # (even though it is put through key derivation functions).
435
+ #
436
+ # Once generated (or regenerated) a potentially weak key should live only as
437
+ # long as it takes for it to encrypt a strong key. The strong key can then
438
+ # be used to encrypt valuable assets.
439
+ #
440
+ # <b>Enforcing Strong Key Size</b>
441
+ #
442
+ # If one key is potentially weaker than the other, the weaker key must be this
443
+ # object and the strong key is reconstituted and returned by this method.
444
+ #
445
+ # @param ciphertext_to_decrypt [String]
446
+ # Provide the ciphertext produced by our sister key encryption method.
447
+ # The ciphertext should hold 96 bytes which equates to 128 base64 characters.
448
+ # The random initialization vector (iv) accounts for the first 16 bytes.
449
+ # The actual crypt ciphertext then accounts for the final 80 bytes.
450
+ #
451
+ # @return [Key]
452
+ # return the key that was serialized into base64 and then encrypted (locked down)
453
+ # with the 256 bit binary symmetric encryption key from this host object.
454
+ #
455
+ # @raise [ArgumentError]
456
+ # the size of the parameter ciphertext must be 128 base 64 characters.
457
+ def do_decrypt_key ciphertext_to_decrypt
458
+
459
+ bit_text = Key64.to_bits(ciphertext_to_decrypt)
460
+ size_msg = "Expected bit count is #{EXPECTED_CIPHER_BIT_LENGTH} not #{bit_text.length}."
461
+ raise RuntimeError, size_msg unless bit_text.length == EXPECTED_CIPHER_BIT_LENGTH
462
+
463
+ cipher_x = OpenSSL::Cipher::AES256.new(:CBC)
464
+ cipher_x.decrypt()
465
+
466
+ rawbytes = [ bit_text ].pack("B*")
467
+
468
+ cipher_x.key = to_aes_key()
469
+ cipher_x.iv = rawbytes[ 0 .. ( RANDOM_IV_BYTE_COUNT - 1 ) ]
470
+ key_chars_64 = cipher_x.update( rawbytes[ RANDOM_IV_BYTE_COUNT .. -1 ] ) + cipher_x.final
471
+
472
+ return Key.from_char64( key_chars_64 )
473
+
474
+ end
475
+
476
+
477
+ # Use the {OpenSSL::Cipher::AES256} block cipher in CBC mode and the binary
478
+ # 256bit representation of this key to encrypt the parameter plaintext using
479
+ # the parameter random initialization vector.
480
+ #
481
+ # Store the ciphertext provided by this method. To re-acquire (reconstitute)
482
+ # the plaintext use the {do_decrypt_text} decryption method, giving
483
+ # it the same initialization vector and the ciphertext produced here.
484
+ #
485
+ # <b>Only Encrypt Once</b>
486
+ #
487
+ # Despite the initialization vector protecting against switch attacks you
488
+ # should <b>only use this or any other key once</b> to encrypt an object.
489
+ # While it is okay to encrypt small targets using two different keys, it
490
+ # pays not to do the same when the target is large.
491
+ #
492
+ # @param random_iv [String]
493
+ # a randomly generated 16 byte binary string that is to be used as the
494
+ # initialization vector (IV) - this is a requirement for AES encryption
495
+ # in CBC mode - this IV does not need to be treated as a secret
496
+ #
497
+ # @param plain_text [String]
498
+ # the plaintext or binary string to be encrypted. To re-acquire this string
499
+ # use the {do_decrypt_text} decryption method, giving it the same
500
+ # initialization vector (provided in the first parameter) and the ciphertext
501
+ # returned from this method.
502
+ #
503
+ # @return [String]
504
+ # The returned binary ciphertext should be encoded and persisted until such
505
+ # a time as its re-acquisition by authorized parties becomes necessary.
506
+ def do_encrypt_text random_iv, plain_text
507
+
508
+ crypt_cipher = OpenSSL::Cipher::AES256.new(:CBC)
509
+
510
+ crypt_cipher.encrypt()
511
+ crypt_cipher.iv = random_iv
512
+ crypt_cipher.key = to_aes_key()
513
+
514
+ return crypt_cipher.update( plain_text ) + crypt_cipher.final
515
+
516
+ end
517
+
518
+
519
+ # Use the {OpenSSL::Cipher::AES256} block cipher in CBC mode and the binary
520
+ # 256bit representation of this key to decrypt the parameter ciphertext using
521
+ # the parameter random initialization vector.
522
+ #
523
+ # Use this method to re-acquire (reconstitute) the plaintext that was
524
+ # converted to ciphertext by the {do_encrypt_text} encryption method,
525
+ # naturally using the same initialization vector for both calls.
526
+ #
527
+ # <b>Only Decrypt Once</b>
528
+ #
529
+ # Consider <b>a key spent</b> as soon as it decrypts the one object it was
530
+ # created to decrypt. Like a bee dying after a sting, a key should die after
531
+ # it decrypts an object. Should re-decryption be necessary - another key
532
+ # should be derived or generated.
533
+ #
534
+ # @param random_iv [String]
535
+ # a randomly generated 16 byte binary string that is to be used as the
536
+ # initialization vector (IV) - this is a requirement for AES decryption
537
+ # in CBC mode - this IV does not need to be treated as a secret
538
+ #
539
+ # @param cipher_text [String]
540
+ # the ciphertext or binary string to be decrypted in order to re-acquire
541
+ # (reconstitute) the plaintext that was converted to ciphertext by the
542
+ # {do_encrypt_text} encryption method.
543
+ #
544
+ # @return [String]
545
+ # if the plaintext (or binary string) returned here still needs to be
546
+ # kept on the low, derive or generate another key to protect it.
547
+ def do_decrypt_text random_iv, cipher_text
548
+
549
+ raise ArgumentError, "Incoming cipher text cannot be nil." if cipher_text.nil?
550
+
551
+ crypt_cipher = OpenSSL::Cipher::AES256.new(:CBC)
552
+
553
+ crypt_cipher.decrypt()
554
+ crypt_cipher.iv = random_iv
555
+ crypt_cipher.key = to_aes_key()
556
+
557
+ return crypt_cipher.update( cipher_text ) + crypt_cipher.final
558
+
559
+ end
560
+
561
+
562
+ private
563
+
564
+
565
+ RANDOM_KEY_BYTE_LENGTH = 48
566
+
567
+ EIGHT_BIT_INTEGER_SIZE = 256
568
+
569
+ RANDOM_IV_BYTE_COUNT = 16
570
+
571
+ CIPHERTEXT_BYTE_COUNT = 80
572
+
573
+ EXPECTED_CIPHER_BIT_LENGTH = ( CIPHERTEXT_BYTE_COUNT + RANDOM_IV_BYTE_COUNT ) * 8
574
+
575
+
576
+ def assert_non_nil_bits
577
+ nil_err_msg = "The bit string for this key is nil."
578
+ raise RuntimeError, nil_err_msg if @bit_string.nil?
579
+ end
580
+
581
+
582
+ end
583
+
584
+
585
+ end